示例#1
0
class AirPlayPlayerTest(AioHTTPTestCase):
    def setUp(self):
        AioHTTPTestCase.setUp(self)
        self.log_handler = LogOutputHandler(self)

        # This is a hack that overrides asyncio.sleep to avoid making the test
        # slow. It also counts number of calls, since this is quite important
        # to the general function.
        player.asyncio.sleep = self.fake_asyncio_sleep
        self.no_of_sleeps = 0

    def tearDown(self):
        AioHTTPTestCase.tearDown(self)
        self.log_handler.tearDown()

    @asyncio.coroutine
    def get_application(self, loop=None):
        self.fake_atv = FakeDaapAppleTV(self.loop, 0, 0, 0, self)
        self.usecase = AppleTVUseCases(self.fake_atv)

        # Import TestServer here and not globally, otherwise py.test will
        # complain when running:
        #
        #   test_functional.py cannot collect test class 'TestServer'
        #   because it has a __init__ constructor
        from aiohttp.test_utils import TestServer
        return TestServer(self.fake_atv)

    @asyncio.coroutine
    def fake_asyncio_sleep(self, time, loop):
        self.no_of_sleeps += 1

    @unittest_run_loop
    def test_play_video(self):
        self.usecase.airplay_playback_idle()
        self.usecase.airplay_playback_playing()
        self.usecase.airplay_playback_idle()

        aplay = player.AirPlayPlayer(self.loop,
                                     '127.0.0.1',
                                     port=self.app.port)
        yield from aplay.play_url(STREAM, position=START_POSITION)

        self.assertEqual(self.fake_atv.last_airplay_url, STREAM)
        self.assertEqual(self.fake_atv.last_airplay_start, START_POSITION)
        self.assertEqual(self.no_of_sleeps, 2)  # playback + idle = 3

    @unittest_run_loop
    def test_play_when_device_not_ready(self):
        self.usecase.airplay_playback_not_ready()

        aplay = player.AirPlayPlayer(self.loop,
                                     '127.0.0.1',
                                     port=self.app.port)
        yield from aplay.play_url(STREAM, position=START_POSITION)

        self.assertEqual(self.no_of_sleeps, 0)
示例#2
0
class AirPlayPlayerTest(AioHTTPTestCase):

    def setUp(self):
        AioHTTPTestCase.setUp(self)
        self.log_handler = LogOutputHandler(self)
        self.session = ClientSession(loop=self.loop)

    def tearDown(self):
        self.session.close()
        self.log_handler.tearDown()
        AioHTTPTestCase.tearDown(self)

    @asyncio.coroutine
    def get_application(self, loop=None):
        self.fake_atv = FakeAppleTV(self.loop, 0, 0, 0, self)

        # Import TestServer here and not globally, otherwise py.test will
        # complain when running:
        #
        #   test_functional.py cannot collect test class 'TestServer'
        #   because it has a __init__ constructor
        from aiohttp.test_utils import TestServer
        return TestServer(self.fake_atv)

    @unittest_run_loop
    def test_verify_invalid(self):
        http = HttpSession(
            self.session, 'http://127.0.0.1:{0}/'.format(self.app.port))
        handler = srp.SRPAuthHandler()
        handler.initialize(INVALID_AUTH_KEY)

        verifier = AuthenticationVerifier(http, handler)
        with self.assertRaises(DeviceAuthenticationError):
            yield from verifier.verify_authed()

    @unittest_run_loop
    def test_verify_authenticated(self):
        http = HttpSession(
            self.session, 'http://127.0.0.1:{0}/'.format(self.app.port))
        handler = srp.SRPAuthHandler()
        handler.initialize(binascii.unhexlify(DEVICE_AUTH_KEY))

        verifier = AuthenticationVerifier(http, handler)
        self.assertTrue((yield from verifier.verify_authed()))

    @unittest_run_loop
    def test_auth_successful(self):
        http = HttpSession(
            self.session, 'http://127.0.0.1:{0}/'.format(self.app.port))
        handler = srp.SRPAuthHandler()
        handler.initialize(INVALID_AUTH_KEY)

        auther = DeviceAuthenticator(http, handler)
        yield from auther.start_authentication()
        with self.assertRaises(DeviceAuthenticationError):
            yield from auther.finish_authentication(
                DEVICE_IDENTIFIER, DEVICE_PIN)

    @unittest_run_loop
    def test_auth_failed(self):
        http = HttpSession(
            self.session, 'http://127.0.0.1:{0}/'.format(self.app.port))
        handler = srp.SRPAuthHandler()
        handler.initialize(binascii.unhexlify(DEVICE_AUTH_KEY))

        auther = DeviceAuthenticator(http, handler)
        yield from auther.start_authentication()
        self.assertTrue((yield from auther.finish_authentication(
            DEVICE_IDENTIFIER, DEVICE_PIN)))
示例#3
0
class FunctionalTest(AioHTTPTestCase):
    def setUp(self):
        AioHTTPTestCase.setUp(self)
        self.atv = self.get_connected_device(HSGID)
        self.log_handler = LogOutputHandler(self)

        # Make sleep calls do nothing to not slow down tests
        @asyncio.coroutine
        def fake_sleep(self, time=None, loop=None):
            pass

        asyncio.sleep = fake_sleep

        # 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())
        AioHTTPTestCase.tearDown(self)
        self.log_handler.tearDown()

    @asyncio.coroutine
    def get_application(self, loop=None):
        self.fake_atv = FakeAppleTV(self.loop, HSGID, PAIRING_GUID, SESSION_ID,
                                    self)
        self.usecase = AppleTVUseCases(self.fake_atv)

        # Import TestServer here and not globally, otherwise py.test will
        # complain when running:
        #
        #   test_functional.py cannot collect test class 'TestServer'
        #   because it has a __init__ constructor
        from aiohttp.test_utils import TestServer
        return TestServer(self.fake_atv)

    def get_connected_device(self, identifier):
        details = AppleTVDevice('Apple TV', '127.0.0.1', identifier,
                                self.app.port)
        return connect_to_apple_tv(details, self.loop)

    # 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
    def test_pairing_with_device(self):
        zeroconf = zeroconf_stub.stub(pairing)
        self.usecase.pairing_response(REMOTE_NAME, PAIRINGCODE)

        handler = pyatv.pair_with_apple_tv(
            self.loop,
            PIN_CODE,
            REMOTE_NAME,
            pairing_guid=pairing.DEFAULT_PAIRING_GUID)
        yield from handler.start(zeroconf)
        yield from self.usecase.act_on_bonjour_services(zeroconf)
        yield from handler.stop()

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

    @unittest_run_loop
    def test_play_url(self):
        self.usecase.airplay_playback_idle()
        self.usecase.airplay_playback_playing()
        self.usecase.airplay_playback_idle()

        yield from self.atv.airplay.play_url(AIRPLAY_STREAM,
                                             port=self.app.port)

        self.assertEqual(self.fake_atv.last_airplay_url, AIRPLAY_STREAM)

    @unittest_run_loop
    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):
            yield from 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
    def test_relogin_if_session_expired(self):
        yield from 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 = yield from self.atv.metadata.artwork()
        self.assertEqual(artwork, EXPECTED_ARTWORK)

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

    @unittest_run_loop
    def test_login_with_pairing_guid_succeed(self):
        yield from self.atv.logout()
        self.atv = self.get_connected_device(PAIRING_GUID)
        session_id = yield from self.atv.login()
        self.assertEqual(SESSION_ID, session_id)

    # When moving around using the arrow keys, a sequence of seven
    # different requests are sent to the device. To simplify the
    # test, verify that seven commands were sent and that the last
    # command matches the expected arrow key. This does not guarantee
    # that the earlier six commands were correct, but it's good
    # enough to keep the tests clean.

    @unittest_run_loop
    def test_button_up(self):
        yield from self.atv.remote_control.up()
        self.assertEqual(self.fake_atv.buttons_press_count, 7)
        self.assertEqual(self.fake_atv.last_button_pressed, 'up')

    @unittest_run_loop
    def test_button_down(self):
        yield from self.atv.remote_control.down()
        self.assertEqual(self.fake_atv.buttons_press_count, 7)
        self.assertEqual(self.fake_atv.last_button_pressed, 'down')

    @unittest_run_loop
    def test_button_left(self):
        yield from self.atv.remote_control.left()
        self.assertEqual(self.fake_atv.buttons_press_count, 7)
        self.assertEqual(self.fake_atv.last_button_pressed, 'left')

    @unittest_run_loop
    def test_button_right(self):
        yield from self.atv.remote_control.right()
        self.assertEqual(self.fake_atv.buttons_press_count, 7)
        self.assertEqual(self.fake_atv.last_button_pressed, 'right')

    @unittest_run_loop
    def test_button_play(self):
        yield from self.atv.remote_control.play()
        self.assertEqual(self.fake_atv.last_button_pressed, 'play')

    @unittest_run_loop
    def test_button_pause(self):
        yield from self.atv.remote_control.pause()
        self.assertEqual(self.fake_atv.last_button_pressed, 'pause')

    @unittest_run_loop
    def test_button_stop(self):
        yield from self.atv.remote_control.stop()
        self.assertEqual(self.fake_atv.last_button_pressed, 'stop')

    @unittest_run_loop
    def test_button_next(self):
        yield from self.atv.remote_control.next()
        self.assertEqual(self.fake_atv.last_button_pressed, 'nextitem')

    @unittest_run_loop
    def test_button_previous(self):
        yield from self.atv.remote_control.previous()
        self.assertEqual(self.fake_atv.last_button_pressed, 'previtem')

    @unittest_run_loop
    def test_button_select(self):
        yield from self.atv.remote_control.select()
        self.assertEqual(self.fake_atv.last_button_pressed, 'select')

    @unittest_run_loop
    def test_button_menu(self):
        yield from self.atv.remote_control.menu()
        self.assertEqual(self.fake_atv.last_button_pressed, 'menu')

    @unittest_run_loop
    def test_button_top_menu(self):
        yield from self.atv.remote_control.top_menu()
        self.assertEqual(self.fake_atv.last_button_pressed, 'topmenu')

    @unittest_run_loop
    def test_metadata_artwork(self):
        self.usecase.change_artwork(EXPECTED_ARTWORK)

        artwork = yield from self.atv.metadata.artwork()
        self.assertEqual(artwork, EXPECTED_ARTWORK)

    @unittest_run_loop
    def test_metadata_artwork_url(self):
        self.usecase.change_artwork(EXPECTED_ARTWORK)

        # Must be logged in to have valid session id
        yield from self.atv.login()

        # URL to artwork
        artwork_url = yield from self.atv.metadata.artwork_url()

        # Fetch artwork with a GET request to ensure it works
        artwork, _ = yield from utils.simple_get(artwork_url, self.loop)
        self.assertEqual(artwork, EXPECTED_ARTWORK)

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

        artwork = yield from self.atv.metadata.artwork()
        self.assertIsNone(artwork)

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

        playing = yield from self.atv.metadata.playing()
        self.assertEqual(playing.media_type, const.MEDIA_TYPE_UNKNOWN)
        self.assertEqual(playing.play_state, const.PLAY_STATE_NO_MEDIA)

    @unittest_run_loop
    def test_metadata_video_paused(self):
        self.usecase.video_playing(paused=True,
                                   title='dummy',
                                   total_time=123,
                                   position=3)

        playing = yield from self.atv.metadata.playing()
        self.assertEqual(playing.media_type, const.MEDIA_TYPE_VIDEO)
        self.assertEqual(playing.play_state, const.PLAY_STATE_PAUSED)
        self.assertEqual(playing.title, 'dummy')
        self.assertEqual(playing.total_time, 123)
        self.assertEqual(playing.position, 3)

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

        playing = yield from self.atv.metadata.playing()
        self.assertEqual(playing.media_type, const.MEDIA_TYPE_VIDEO)
        self.assertEqual(playing.play_state, const.PLAY_STATE_PLAYING)
        self.assertEqual(playing.title, 'video')
        self.assertEqual(playing.total_time, 40)
        self.assertEqual(playing.position, 10)

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

        playing = yield from self.atv.metadata.playing()
        self.assertEqual(playing.media_type, const.MEDIA_TYPE_MUSIC)
        self.assertEqual(playing.play_state, const.PLAY_STATE_PAUSED)
        self.assertEqual(playing.title, 'music')
        self.assertEqual(playing.artist, 'artist')
        self.assertEqual(playing.album, 'album')
        self.assertEqual(playing.total_time, 222)
        self.assertEqual(playing.position, 49)

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

        playing = yield from self.atv.metadata.playing()
        self.assertEqual(playing.media_type, const.MEDIA_TYPE_MUSIC)
        self.assertEqual(playing.play_state, const.PLAY_STATE_PLAYING)
        self.assertEqual(playing.title, 'music')
        self.assertEqual(playing.artist, 'test1')
        self.assertEqual(playing.album, 'test2')
        self.assertEqual(playing.total_time, 2)
        self.assertEqual(playing.position, 1)

    @unittest_run_loop
    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")
        yield from self.atv.metadata.playing()

        # Setup push updates which will instantly get the next one ("video2")
        listener = PushListener()
        self.atv.push_updater.listener = listener
        yield from 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
    def test_shuffle_state(self):
        self.usecase.example_video(shuffle=False)
        self.usecase.example_video(shuffle=True)

        playing = yield from self.atv.metadata.playing()
        self.assertFalse(playing.shuffle)

        playing = yield from self.atv.metadata.playing()
        self.assertTrue(playing.shuffle)

    @unittest_run_loop
    def test_repeat_state(self):
        self.usecase.example_video(repeat=const.REPEAT_STATE_OFF)
        self.usecase.example_video(repeat=const.REPEAT_STATE_TRACK)
        self.usecase.example_video(repeat=const.REPEAT_STATE_ALL)

        playing = yield from self.atv.metadata.playing()
        self.assertEqual(playing.repeat, const.REPEAT_STATE_OFF)

        playing = yield from self.atv.metadata.playing()
        self.assertEqual(playing.repeat, const.REPEAT_STATE_TRACK)

        playing = yield from self.atv.metadata.playing()
        self.assertEqual(playing.repeat, const.REPEAT_STATE_ALL)

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

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

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

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

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

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

        playing = yield from self.atv.metadata.playing()
        self.assertEqual(playing.play_state, const.PLAY_STATE_LOADING)
示例#4
0
class FunctionalTest(AioHTTPTestCase):
    def setUp(self):
        AioHTTPTestCase.setUp(self)
        self.atv = self.get_connected_device()
        self.log_handler = LogOutputHandler(self)

    def tearDown(self):
        AioHTTPTestCase.tearDown(self)
        self.log_handler.tearDown()

    def get_app(self, loop):
        self.fake_atv = FakeAppleTV(loop, HSGID, SESSION_ID, self)
        self.usecase = AppleTVUseCases(self.fake_atv)

        # Import TestServer here and not globally, otherwise py.test will
        # complain when running:
        #
        #   test_functional.py cannot collect test class 'TestServer'
        #   because it has a __init__ constructor
        from aiohttp.test_utils import TestServer
        return TestServer(self.fake_atv)

    def get_connected_device(self):
        details = AppleTVDevice('Apple TV', '127.0.0.1', HSGID, self.app.port)
        return connect_to_apple_tv(details, self.loop)

    @unittest_run_loop
    def test_login_failed(self):
        self.usecase.make_login_fail()

        with self.assertRaises(exceptions.AuthenticationError):
            yield from self.atv.login()

        yield from self.atv.logout()

    @unittest_run_loop
    def test_login_succeed(self):
        session_id = yield from self.atv.login()
        self.assertEqual(SESSION_ID, session_id)
        yield from self.atv.logout()

    @unittest_run_loop
    def test_button_play(self):
        yield from self.atv.remote_control.play()
        self.assertEqual(self.fake_atv.last_button_pressed, 'play')
        yield from self.atv.logout()

    @unittest_run_loop
    def test_button_pause(self):
        yield from self.atv.remote_control.pause()
        self.assertEqual(self.fake_atv.last_button_pressed, 'pause')
        yield from self.atv.logout()

    @unittest_run_loop
    def test_button_next(self):
        yield from self.atv.remote_control.next()
        self.assertEqual(self.fake_atv.last_button_pressed, 'nextitem')
        yield from self.atv.logout()

    @unittest_run_loop
    def test_button_previous(self):
        yield from self.atv.remote_control.previous()
        self.assertEqual(self.fake_atv.last_button_pressed, 'previtem')
        yield from self.atv.logout()

    @unittest_run_loop
    def test_button_select(self):
        yield from self.atv.remote_control.select()
        self.assertEqual(self.fake_atv.last_button_pressed, 'select')
        yield from self.atv.logout()

    @unittest_run_loop
    def test_button_menu(self):
        yield from self.atv.remote_control.menu()
        self.assertEqual(self.fake_atv.last_button_pressed, 'menu')
        yield from self.atv.logout()

    @unittest_run_loop
    def test_button_topmenu(self):
        yield from self.atv.remote_control.topmenu()
        self.assertEqual(self.fake_atv.last_button_pressed, 'topmenu')
        yield from self.atv.logout()

    @unittest_run_loop
    def test_metadata_artwork(self):
        expected_artwork = b'12345'
        self.usecase.change_artwork(expected_artwork)

        artwork = yield from self.atv.metadata.artwork()
        self.assertEqual(artwork, expected_artwork)
        yield from self.atv.logout()

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

        artwork = yield from self.atv.metadata.artwork()
        self.assertIsNone(artwork)
        yield from self.atv.logout()

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

        playing = yield from self.atv.metadata.playing()
        self.assertEqual(playing.media_type, const.MEDIA_TYPE_UNKNOWN)
        self.assertEqual(playing.play_state, const.PLAY_STATE_NO_MEDIA)

        yield from self.atv.logout()

    @unittest_run_loop
    def test_metadata_video_paused(self):
        self.usecase.video_playing(paused=True,
                                   title='dummy',
                                   total_time=123,
                                   position=3)

        playing = yield from self.atv.metadata.playing()
        self.assertEqual(playing.media_type, const.MEDIA_TYPE_VIDEO)
        self.assertEqual(playing.play_state, const.PLAY_STATE_PAUSED)
        self.assertEqual(playing.title, 'dummy')
        self.assertEqual(playing.total_time, 123)
        self.assertEqual(playing.position, 3)

        yield from self.atv.logout()

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

        playing = yield from self.atv.metadata.playing()
        self.assertEqual(playing.media_type, const.MEDIA_TYPE_VIDEO)
        self.assertEqual(playing.play_state, const.PLAY_STATE_PLAYING)
        self.assertEqual(playing.title, 'video')
        self.assertEqual(playing.total_time, 40)
        self.assertEqual(playing.position, 10)

        yield from self.atv.logout()

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

        playing = yield from self.atv.metadata.playing()
        self.assertEqual(playing.media_type, const.MEDIA_TYPE_MUSIC)
        self.assertEqual(playing.play_state, const.PLAY_STATE_PAUSED)
        self.assertEqual(playing.title, 'music')
        self.assertEqual(playing.artist, 'artist')
        self.assertEqual(playing.album, 'album')
        self.assertEqual(playing.total_time, 222)
        self.assertEqual(playing.position, 49)

        yield from self.atv.logout()

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

        playing = yield from self.atv.metadata.playing()
        self.assertEqual(playing.media_type, const.MEDIA_TYPE_MUSIC)
        self.assertEqual(playing.play_state, const.PLAY_STATE_PLAYING)
        self.assertEqual(playing.title, 'music')
        self.assertEqual(playing.artist, 'test1')
        self.assertEqual(playing.album, 'test2')
        self.assertEqual(playing.total_time, 2)
        self.assertEqual(playing.position, 1)

        yield from self.atv.logout()

    @unittest_run_loop
    def test_seek_in_playing_media(self):
        yield from self.atv.remote_control.set_position(60)
        self.assertEqual(self.fake_atv.responses['playing'].position, 60000)
        yield from self.atv.logout()

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

        playing = yield from self.atv.metadata.playing()
        self.assertEqual(playing.play_state, const.PLAY_STATE_LOADING)
        yield from self.atv.logout()