예제 #1
0
class DmapPairFunctionalTest(AioHTTPTestCase):
    def setUp(self):
        AioHTTPTestCase.setUp(self)
        self.pairing = None

        self.service = DmapService("dmap_id",
                                   PAIRING_GUID,
                                   port=self.server.port)
        self.conf = AppleTV("127.0.0.1", "Apple TV")
        self.conf.add_service(self.service)

        # TODO: currently stubs internal method, should provide stub
        # for netifaces later
        pairing._get_private_ip_addresses = lambda: [
            ipaddress.ip_address("10.0.0.1")
        ]

        self.zeroconf = zeroconf_stub.stub(pyatv.dmap.pairing)

    async def tearDownAsync(self):
        await self.pairing.close()
        await super().tearDownAsync()

    async def get_application(self, loop=None):
        self.fake_atv = FakeAppleTV(HSGID, PAIRING_GUID, SESSION_ID)
        self.usecase = AppleTVUseCases(self.fake_atv)
        return self.fake_atv.app

    async def initiate_pairing(self,
                               name=REMOTE_NAME,
                               pairing_guid=PAIRING_GUID):
        self.usecase.pairing_response(REMOTE_NAME, PAIRINGCODE)

        options = {
            "zeroconf": self.zeroconf,
            "name": name,
            "pairing_guid": pairing_guid,
        }

        self.pairing = await pyatv.pair(self.conf, Protocol.DMAP, self.loop,
                                        **options)

    @unittest_run_loop
    async def test_pairing_with_device(self):
        await self.initiate_pairing()

        self.assertFalse(self.pairing.device_provides_pin)

        await self.pairing.begin()
        self.pairing.pin(PIN_CODE)

        await self.usecase.act_on_bonjour_services(self.zeroconf)

        await self.pairing.finish()

        self.assertTrue(self.pairing.has_paired)
        self.assertEqual(self.service.credentials, PAIRING_GUID)
예제 #2
0
 async def get_application(self, loop=None):
     self.fake_atv = FakeAppleTV(
         HSGID, PAIRING_GUID, SESSION_ID, self)
     self.usecase = AppleTVUseCases(self.fake_atv)
     return self.fake_atv
예제 #3
0
class DMAPFunctionalTest(common_functional_tests.CommonFunctionalTests):

    def setUp(self):
        super().setUp()
        self.atv = self.get_connected_device(HSGID)

        # TODO: currently stubs internal method, should provide stub
        # for netifaces later
        pairing._get_private_ip_addresses = \
            lambda: [ipaddress.ip_address('10.0.0.1')]

    def tearDown(self):
        self.loop.run_until_complete(self.atv.logout())
        super().tearDown()

    async def get_application(self, loop=None):
        self.fake_atv = FakeAppleTV(
            HSGID, PAIRING_GUID, SESSION_ID, self)
        self.usecase = AppleTVUseCases(self.fake_atv)
        return self.fake_atv

    def get_connected_device(self, identifier):
        conf = AppleTV('127.0.0.1', 'Apple TV')
        conf.add_service(DmapService(identifier, port=self.server.port))
        conf.add_service(AirPlayService(self.server.port))
        return connect_to_apple_tv(conf, self.loop)

    @unittest_run_loop
    async def test_not_supportedt(self):
        with self.assertRaises(exceptions.NotSupportedError):
            await self.atv.remote_control.suspend()

    # This is not a pretty test and it does crazy things. Should probably be
    # re-written later but will do for now.
    @unittest_run_loop
    async def test_pairing_with_device(self):
        zeroconf = zeroconf_stub.stub(pairing)
        self.usecase.pairing_response(REMOTE_NAME, PAIRINGCODE)

        self.assertFalse(self.atv.pairing.device_provides_pin)

        await self.atv.pairing.start(zeroconf=zeroconf,
                                     pairing_guid=pairing.DEFAULT_PAIRING_GUID,
                                     name=REMOTE_NAME)
        self.atv.pairing.pin(PIN_CODE)
        await self.usecase.act_on_bonjour_services(zeroconf)

        self.assertTrue(self.atv.pairing.has_paired,
                        msg='did not pair with device')

        await self.atv.pairing.stop()

    @unittest_run_loop
    async def test_login_failed(self):
        # Twice since the client will retry one time
        self.usecase.make_login_fail()
        self.usecase.make_login_fail()

        with self.assertRaises(exceptions.AuthenticationError):
            await self.atv.login()

    # This test verifies issue #2 (automatic re-login). It uses the artwork
    # API, but it could have been any API since the login code is the same.
    @unittest_run_loop
    async def test_relogin_if_session_expired(self):
        await self.atv.login()

        # Here, we are logged in and currently have a asession id. These
        # usescases will result in being logged out (HTTP 403) and forcing a
        # re-login with a new session id (1234)
        self.usecase.force_relogin(1234)
        self.usecase.artwork_no_permission()
        self.usecase.change_artwork(EXPECTED_ARTWORK)

        artwork = await self.atv.metadata.artwork()
        self.assertEqual(artwork, EXPECTED_ARTWORK)

    @unittest_run_loop
    async def test_login_with_hsgid_succeed(self):
        session_id = await self.atv.login()
        self.assertEqual(SESSION_ID, session_id)

    @unittest_run_loop
    async def test_login_with_pairing_guid_succeed(self):
        await self.atv.logout()
        self.atv = self.get_connected_device(PAIRING_GUID)
        session_id = await self.atv.login()
        self.assertEqual(SESSION_ID, session_id)
예제 #4
0
class DMAPFunctionalTest(common_functional_tests.CommonFunctionalTests):
    async def setUpAsync(self):
        await super().setUpAsync()
        self.atv = await self.get_connected_device(HSGID)

        # TODO: currently stubs internal method, should provide stub
        # for netifaces later
        pairing._get_private_ip_addresses = lambda: [
            ipaddress.ip_address("10.0.0.1")
        ]

    async def tearDownAsync(self):
        await self.atv.close()
        await super().tearDownAsync()

    async def get_application(self, loop=None):
        self.fake_atv = FakeAppleTV(HSGID, PAIRING_GUID, SESSION_ID)
        self.usecase = AppleTVUseCases(self.fake_atv)
        return self.fake_atv.app

    async def get_connected_device(self, hsgid):
        self.dmap_service = DmapService("dmap_id",
                                        hsgid,
                                        port=self.server.port)
        self.airplay_service = AirPlayService("airplay_id", self.server.port,
                                              DEVICE_CREDENTIALS)
        self.conf = AppleTV("127.0.0.1", "Apple TV")
        self.conf.add_service(self.dmap_service)
        self.conf.add_service(self.airplay_service)
        return await connect(self.conf, self.loop)

    @unittest_run_loop
    async def test_app_not_supported(self):
        with self.assertRaises(exceptions.NotSupportedError):
            self.atv.metadata.app

    @unittest_run_loop
    async def test_connect_failed(self):
        # Twice since the client will retry one time
        self.usecase.make_login_fail()
        self.usecase.make_login_fail()

        with self.assertRaises(exceptions.AuthenticationError):
            await self.atv.connect()

    # This test verifies issue #2 (automatic re-login). It uses the artwork
    # API, but it could have been any API since the login code is the same.
    @unittest_run_loop
    async def test_relogin_if_session_expired(self):
        await self.atv.connect()

        # Here, we are logged in and currently have a asession id. These
        # usescases will result in being logged out (HTTP 403) and forcing a
        # re-login with a new session id (1234)
        self.usecase.example_video()
        self.usecase.force_relogin(1234)
        self.usecase.artwork_no_permission()
        self.usecase.change_artwork(ARTWORK_BYTES, ARTWORK_MIMETYPE)

        artwork = await self.atv.metadata.artwork()
        self.assertEqual(artwork.bytes, ARTWORK_BYTES)

    @unittest_run_loop
    async def test_login_with_pairing_guid_succeed(self):
        await self.atv.close()
        self.atv = await self.get_connected_device(PAIRING_GUID)
        await self.atv.connect()

    @unittest_run_loop
    async def test_connection_lost(self):
        self.usecase.server_closes_connection()

        self.atv.listener = DummyDeviceListener()
        self.atv.push_updater.listener = DummyPushListener()
        self.atv.push_updater.start()

        # Callback is scheduled on the event loop, so a semaphore is used
        # to synchronize with the loop
        await asyncio.wait_for(self.atv.listener.lost_sem.acquire(),
                               timeout=3.0)

    @unittest_run_loop
    async def test_button_unsupported_raises(self):
        buttons = ["home", "volume_up", "volume_down", "suspend", "wakeup"]
        for button in buttons:
            with self.assertRaises(exceptions.NotSupportedError):
                await getattr(self.atv.remote_control, button)()

    @unittest_run_loop
    async def test_shuffle_state_albums(self):
        # DMAP does not support "albums" as shuffle state, so it is
        # mapped to "songs"
        self.usecase.example_video(shuffle=ShuffleState.Albums)
        playing = await self.playing(shuffle=ShuffleState.Songs)
        self.assertEqual(playing.shuffle, ShuffleState.Songs)

    @unittest_run_loop
    async def test_set_shuffle_albums(self):
        self.usecase.example_video()

        # DMAP does not support "albums" as shuffle state, so it is
        # mapped to "songs"
        await self.atv.remote_control.set_shuffle(ShuffleState.Albums)
        playing = await self.playing(shuffle=ShuffleState.Songs)
        self.assertEqual(playing.shuffle, ShuffleState.Songs)

    @unittest_run_loop
    async def test_play_url_no_service(self):
        conf = AppleTV("127.0.0.1", "Apple TV")
        conf.add_service(self.dmap_service)

        atv = await connect(conf, self.loop)

        with self.assertRaises(exceptions.NotSupportedError):
            await atv.stream.play_url("http://123")

        await atv.close()

    @unittest_run_loop
    async def test_unsupported_power_state(self):

        # Check if power state return PowerState.Unknown as expected
        self.assertEqual(self.atv.power.power_state, PowerState.Unknown)

        # Call turn_on and check for exception
        with self.assertRaises(exceptions.NotSupportedError):
            await self.atv.power.turn_on()

        # Call turn_off and check for exception
        with self.assertRaises(exceptions.NotSupportedError):
            await self.atv.power.turn_off()

    @unittest_run_loop
    async def test_basic_device_info(self):
        self.assertEqual(self.atv.device_info.operating_system,
                         OperatingSystem.Legacy)

    @unittest_run_loop
    async def test_always_available_features(self):
        self.assertFeatures(
            FeatureState.Available,
            FeatureName.Down,
            FeatureName.Left,
            FeatureName.Menu,
            FeatureName.Right,
            FeatureName.Select,
            FeatureName.TopMenu,
            FeatureName.Up,
        )

    @unittest_run_loop
    async def test_unsupported_features(self):
        self.assertFeatures(
            FeatureState.Unsupported,
            FeatureName.VolumeUp,
            FeatureName.VolumeDown,
            FeatureName.Home,
            FeatureName.HomeHold,
            FeatureName.Suspend,
            FeatureName.WakeUp,
            FeatureName.PowerState,
            FeatureName.TurnOn,
            FeatureName.TurnOff,
            FeatureName.App,
        )

    @unittest_run_loop
    async def test_always_unknown_features(self):
        self.assertFeatures(
            FeatureState.Unknown,
            FeatureName.Artwork,
            FeatureName.Next,
            FeatureName.Pause,
            FeatureName.Play,
            FeatureName.PlayPause,
            FeatureName.Previous,
            FeatureName.SetPosition,
            FeatureName.SetRepeat,
            FeatureName.SetShuffle,
            FeatureName.Stop,
        )

    @unittest_run_loop
    async def test_features_shuffle_repeat(self):
        self.usecase.nothing_playing()
        await self.playing()

        self.assertFeatures(
            FeatureState.Unavailable,
            FeatureName.Shuffle,
            FeatureName.Repeat,
        )

        self.usecase.example_music(shuffle=ShuffleState.Albums,
                                   repeat=RepeatState.Track)
        await self.playing(title="music")

        self.assertFeatures(
            FeatureState.Available,
            FeatureName.Shuffle,
            FeatureName.Repeat,
        )
예제 #5
0
class DMAPFunctionalTest(common_functional_tests.CommonFunctionalTests):
    async def setUpAsync(self):
        await super().setUpAsync()
        self.atv = await self.get_connected_device(HSGID)

        # TODO: currently stubs internal method, should provide stub
        # for netifaces later
        pairing._get_private_ip_addresses = \
            lambda: [ipaddress.ip_address('10.0.0.1')]

    async def tearDownAsync(self):
        await self.atv.close()
        await super().tearDownAsync()

    async def get_application(self, loop=None):
        self.fake_atv = FakeAppleTV(HSGID, PAIRING_GUID, SESSION_ID, self)
        self.usecase = AppleTVUseCases(self.fake_atv)
        return self.fake_atv.app

    async def get_connected_device(self, hsgid):
        self.dmap_service = DmapService('dmap_id',
                                        hsgid,
                                        port=self.server.port)
        self.airplay_service = AirPlayService('airplay_id', self.server.port,
                                              DEVICE_CREDENTIALS)
        self.conf = AppleTV('127.0.0.1', 'Apple TV')
        self.conf.add_service(self.dmap_service)
        self.conf.add_service(self.airplay_service)
        return await pyatv.connect(self.conf, self.loop)

    @unittest_run_loop
    async def test_not_supportedt(self):
        with self.assertRaises(exceptions.NotSupportedError):
            await self.atv.remote_control.suspend()

    @unittest_run_loop
    async def test_connect_failed(self):
        # Twice since the client will retry one time
        self.usecase.make_login_fail()
        self.usecase.make_login_fail()

        with self.assertRaises(exceptions.AuthenticationError):
            await self.atv.connect()

    # This test verifies issue #2 (automatic re-login). It uses the artwork
    # API, but it could have been any API since the login code is the same.
    @unittest_run_loop
    async def test_relogin_if_session_expired(self):
        await self.atv.connect()

        # Here, we are logged in and currently have a asession id. These
        # usescases will result in being logged out (HTTP 403) and forcing a
        # re-login with a new session id (1234)
        self.usecase.force_relogin(1234)
        self.usecase.artwork_no_permission()
        self.usecase.change_artwork(ARTWORK_BYTES, ARTWORK_MIMETYPE)

        artwork = await self.atv.metadata.artwork()
        self.assertEqual(artwork.bytes, ARTWORK_BYTES)

    @unittest_run_loop
    async def test_login_with_hsgid_succeed(self):
        session_id = await self.atv.connect()
        self.assertEqual(SESSION_ID, session_id)

    @unittest_run_loop
    async def test_login_with_pairing_guid_succeed(self):
        await self.atv.close()
        self.atv = await self.get_connected_device(PAIRING_GUID)
        session_id = await self.atv.connect()
        self.assertEqual(SESSION_ID, session_id)

    @unittest_run_loop
    async def test_connection_closed(self):
        self.usecase.video_playing(paused=False,
                                   title='video1',
                                   total_time=40,
                                   position=10,
                                   revision=0)

        self.atv.listener = DummyDeviceListener()
        self.atv.push_updater.listener = DummyPushListener()
        await self.atv.push_updater.start()

        # Callback is scheduled on the event loop, so a semaphore is used
        # to synchronize with the loop
        await asyncio.wait_for(self.atv.listener.closed_sem.acquire(),
                               timeout=3.0)

    @unittest_run_loop
    async def test_connection_lost(self):
        self.usecase.server_closes_connection()

        self.atv.listener = DummyDeviceListener()
        self.atv.push_updater.listener = DummyPushListener()
        await self.atv.push_updater.start()

        # Callback is scheduled on the event loop, so a semaphore is used
        # to synchronize with the loop
        await asyncio.wait_for(self.atv.listener.lost_sem.acquire(),
                               timeout=3.0)

    # Common tests are below. Move tests that have been implemented to
    # common_functional_tests.py once implemented

    # TODO: This should check that device_id is one of the IDs
    #       passed to the services into the device.
    def test_metadata_device_id(self):
        self.assertEqual(self.atv.metadata.device_id, 'dmap_id')

    @unittest_run_loop
    async def test_metadata_artwork(self):
        self.usecase.change_artwork(ARTWORK_BYTES, ARTWORK_MIMETYPE)

        artwork = await self.atv.metadata.artwork()
        self.assertIsNotNone(artwork)
        self.assertEqual(artwork.bytes, ARTWORK_BYTES)
        self.assertEqual(artwork.mimetype, ARTWORK_MIMETYPE)

    @unittest_run_loop
    async def test_metadata_artwork_none_if_not_available(self):
        self.usecase.change_artwork(b'', None)

        artwork = await self.atv.metadata.artwork()
        self.assertIsNone(artwork)

    @unittest_run_loop
    async def test_metadata_none_type_when_not_playing(self):
        self.usecase.nothing_playing()

        playing = await self.atv.metadata.playing()
        self.assertEqual(playing.media_type, MediaType.Unknown)
        self.assertEqual(playing.device_state, DeviceState.Idle)

    @unittest_run_loop
    async def test_metadata_video_playing(self):
        self.usecase.video_playing(paused=False,
                                   title='video',
                                   total_time=40,
                                   position=10)

        playing = await self.atv.metadata.playing()
        self.assertEqual(playing.media_type, MediaType.Video)
        self.assertEqual(playing.device_state, DeviceState.Playing)
        self.assertEqual(playing.title, 'video')
        self.assertEqual(playing.total_time, 40)
        self.assertEqual(playing.position, 10)

    @unittest_run_loop
    async def test_metadata_music_paused(self):
        self.usecase.music_playing(paused=True,
                                   title='music',
                                   artist='artist',
                                   album='album',
                                   total_time=222,
                                   position=49,
                                   genre='genre')

        playing = await self.atv.metadata.playing()
        self.assertEqual(playing.media_type, MediaType.Music)
        self.assertEqual(playing.device_state, DeviceState.Paused)
        self.assertEqual(playing.title, 'music')
        self.assertEqual(playing.artist, 'artist')
        self.assertEqual(playing.album, 'album')
        self.assertEqual(playing.genre, 'genre')
        self.assertEqual(playing.total_time, 222)
        self.assertEqual(playing.position, 49)

    @unittest_run_loop
    async def test_metadata_music_playing(self):
        self.usecase.music_playing(paused=False,
                                   title='music',
                                   artist='test1',
                                   album='test2',
                                   total_time=2,
                                   position=1,
                                   genre='genre')

        playing = await self.atv.metadata.playing()
        self.assertEqual(playing.media_type, MediaType.Music)
        self.assertEqual(playing.device_state, DeviceState.Playing)
        self.assertEqual(playing.title, 'music')
        self.assertEqual(playing.artist, 'test1')
        self.assertEqual(playing.album, 'test2')
        self.assertEqual(playing.genre, 'genre')
        self.assertEqual(playing.total_time, 2)
        self.assertEqual(playing.position, 1)

    @unittest_run_loop
    async def test_push_updates(self):
        class PushListener:
            def __init__(self):
                self.playing = None

            def playstatus_update(self, updater, playstatus):
                self.playing = playstatus
                updater.stop()

            @staticmethod
            def playstatus_error(updater, exception):
                pass

        # Prepare two playstatus updates in the fake device. Take note: every
        # time start() is called, revision 0 should be used first. This will
        # make sure that we always get a push update instantly. Otherwise we
        # might hang and wait for an update.
        self.usecase.video_playing(paused=False,
                                   title='video1',
                                   total_time=40,
                                   position=10,
                                   revision=0)
        self.usecase.video_playing(paused=True,
                                   title='video2',
                                   total_time=30,
                                   position=20,
                                   revision=0)

        # Poll the first one ("video1")
        await self.atv.metadata.playing()

        # Setup push updates which will instantly get the next one ("video2")
        listener = PushListener()
        self.atv.push_updater.listener = listener
        await self.atv.push_updater.start()

        # Check that we got the right one
        self.assertIsNotNone(listener.playing)
        self.assertEqual(listener.playing.title, 'video2')

    @unittest_run_loop
    async def test_shuffle_state(self):
        self.usecase.example_video(shuffle=False)
        self.usecase.example_video(shuffle=True)

        playing = await self.atv.metadata.playing()
        self.assertFalse(playing.shuffle)

        playing = await self.atv.metadata.playing()
        self.assertTrue(playing.shuffle)

    @unittest_run_loop
    async def test_repeat_state(self):
        self.usecase.example_video(repeat=RepeatState.Off)
        self.usecase.example_video(repeat=RepeatState.Track)
        self.usecase.example_video(repeat=RepeatState.All)

        playing = await self.atv.metadata.playing()
        self.assertEqual(playing.repeat, RepeatState.Off)

        playing = await self.atv.metadata.playing()
        self.assertEqual(playing.repeat, RepeatState.Track)

        playing = await self.atv.metadata.playing()
        self.assertEqual(playing.repeat, RepeatState.All)

    @unittest_run_loop
    async def test_set_shuffle(self):
        await self.atv.remote_control.set_shuffle(1)
        self.assertEqual(self.fake_atv.properties['dacp.shufflestate'], 1)

        await self.atv.remote_control.set_shuffle(0)
        self.assertEqual(self.fake_atv.properties['dacp.shufflestate'], 0)

    @unittest_run_loop
    async def test_set_repeat(self):
        await self.atv.remote_control.set_repeat(1)
        self.assertEqual(self.fake_atv.properties['dacp.repeatstate'], 1)

        await self.atv.remote_control.set_repeat(2)
        self.assertEqual(self.fake_atv.properties['dacp.repeatstate'], 2)

    @unittest_run_loop
    async def test_seek_in_playing_media(self):
        await self.atv.remote_control.set_position(60)
        self.assertEqual(self.fake_atv.properties['dacp.playingtime'], 60000)

    @unittest_run_loop
    async def test_metadata_loading(self):
        self.usecase.media_is_loading()

        playing = await self.atv.metadata.playing()
        self.assertEqual(playing.device_state, DeviceState.Loading)

    @unittest_run_loop
    async def test_button_unsupported_raises(self):
        buttons = ['home', 'volume_up', 'volume_down', 'suspend', 'wakeup']
        for button in buttons:
            with self.assertRaises(exceptions.NotSupportedError):
                await getattr(self.atv.remote_control, button)()
예제 #6
0
class DMAPFunctionalTest(common_functional_tests.CommonFunctionalTests):
    async def setUpAsync(self):
        await super().setUpAsync()
        self.atv = await self.get_connected_device(HSGID)

        # TODO: currently stubs internal method, should provide stub
        # for netifaces later
        pairing._get_private_ip_addresses = \
            lambda: [ipaddress.ip_address('10.0.0.1')]

    async def tearDownAsync(self):
        await self.atv.close()
        await super().tearDownAsync()

    async def get_application(self, loop=None):
        self.fake_atv = FakeAppleTV(HSGID, PAIRING_GUID, SESSION_ID, self)
        self.usecase = AppleTVUseCases(self.fake_atv)
        return self.fake_atv.app

    async def get_connected_device(self, hsgid):
        self.dmap_service = DmapService('dmap_id',
                                        hsgid,
                                        port=self.server.port)
        self.airplay_service = AirPlayService('airplay_id', self.server.port,
                                              DEVICE_CREDENTIALS)
        self.conf = AppleTV('127.0.0.1', 'Apple TV')
        self.conf.add_service(self.dmap_service)
        self.conf.add_service(self.airplay_service)
        return await connect(self.conf, self.loop)

    @unittest_run_loop
    async def test_not_supportedt(self):
        with self.assertRaises(exceptions.NotSupportedError):
            await self.atv.remote_control.suspend()

    @unittest_run_loop
    async def test_connect_failed(self):
        # Twice since the client will retry one time
        self.usecase.make_login_fail()
        self.usecase.make_login_fail()

        with self.assertRaises(exceptions.AuthenticationError):
            await self.atv.connect()

    # This test verifies issue #2 (automatic re-login). It uses the artwork
    # API, but it could have been any API since the login code is the same.
    @unittest_run_loop
    async def test_relogin_if_session_expired(self):
        await self.atv.connect()

        # Here, we are logged in and currently have a asession id. These
        # usescases will result in being logged out (HTTP 403) and forcing a
        # re-login with a new session id (1234)
        self.usecase.example_video()
        self.usecase.force_relogin(1234)
        self.usecase.artwork_no_permission()
        self.usecase.change_artwork(ARTWORK_BYTES, ARTWORK_MIMETYPE)

        artwork = await self.atv.metadata.artwork()
        self.assertEqual(artwork.bytes, ARTWORK_BYTES)

    @unittest_run_loop
    async def test_login_with_hsgid_succeed(self):
        session_id = await self.atv.connect()
        self.assertEqual(SESSION_ID, session_id)

    @unittest_run_loop
    async def test_login_with_pairing_guid_succeed(self):
        await self.atv.close()
        self.atv = await self.get_connected_device(PAIRING_GUID)
        session_id = await self.atv.connect()
        self.assertEqual(SESSION_ID, session_id)

    @unittest_run_loop
    async def test_connection_lost(self):
        self.usecase.server_closes_connection()

        self.atv.listener = DummyDeviceListener()
        self.atv.push_updater.listener = DummyPushListener()
        self.atv.push_updater.start()

        # Callback is scheduled on the event loop, so a semaphore is used
        # to synchronize with the loop
        await asyncio.wait_for(self.atv.listener.lost_sem.acquire(),
                               timeout=3.0)

    @unittest_run_loop
    async def test_button_unsupported_raises(self):
        buttons = ['home', 'volume_up', 'volume_down', 'suspend', 'wakeup']
        for button in buttons:
            with self.assertRaises(exceptions.NotSupportedError):
                await getattr(self.atv.remote_control, button)()

    @unittest_run_loop
    async def test_shuffle_state_albums(self):
        # DMAP does not support "albums" as shuffle state, so it is
        # mapped to "songs"
        self.usecase.example_video(shuffle=ShuffleState.Albums)
        playing = await self.playing(shuffle=ShuffleState.Songs)
        self.assertEqual(playing.shuffle, ShuffleState.Songs)

    @unittest_run_loop
    async def test_set_shuffle_albums(self):
        self.usecase.example_video()

        # DMAP does not support "albums" as shuffle state, so it is
        # mapped to "songs"
        await self.atv.remote_control.set_shuffle(ShuffleState.Albums)
        playing = await self.playing(shuffle=ShuffleState.Songs)
        self.assertEqual(playing.shuffle, ShuffleState.Songs)

    @unittest_run_loop
    async def test_play_url_no_service(self):
        conf = AppleTV('127.0.0.1', 'Apple TV')
        conf.add_service(self.dmap_service)

        atv = await connect(conf, self.loop)

        with self.assertRaises(exceptions.NotSupportedError):
            await atv.stream.play_url('http://123')

        await atv.close()

    @unittest_run_loop
    async def test_unsupported_power_state(self):

        # Check if power state return PowerState.Unknown as expected
        self.assertEqual(self.atv.power.power_state, PowerState.Unknown)

        # Call turn_on and check for exception
        with self.assertRaises(exceptions.NotSupportedError):
            await self.atv.power.turn_on()

        # Call turn_off and check for exception
        with self.assertRaises(exceptions.NotSupportedError):
            await self.atv.power.turn_off()