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)
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)))
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)
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()