From d962277bc9fa937d6965964e89fd45987a5f25a0 Mon Sep 17 00:00:00 2001 From: Thomas Adamcik Date: Sun, 26 Jan 2014 23:27:30 +0100 Subject: [PATCH] audio: Add stream_changed event plus tests --- mopidy/audio/actor.py | 25 +++++++++++++++++++++++-- mopidy/audio/listener.py | 10 ++++++++++ tests/audio/test_actor.py | 13 +++++++++++++ tests/audio/test_listener.py | 3 +++ 4 files changed, 49 insertions(+), 2 deletions(-) diff --git a/mopidy/audio/actor.py b/mopidy/audio/actor.py index f2003ca0..44794201 100644 --- a/mopidy/audio/actor.py +++ b/mopidy/audio/actor.py @@ -317,7 +317,12 @@ class Audio(pykka.ThreadingActor): str(error).decode('utf-8'), debug.decode('utf-8') or 'None') elif message.type == gst.MESSAGE_ELEMENT: if message.structure.has_name('playbin2-stream-changed'): - logger.debug('Playback of new stream started') + self._on_stream_changed(message) + + def _on_stream_changed(self, message): + uri = message.structure['uri'] + logger.debug('Triggering event: stream_changed(uri=%s)', uri) + AudioListener.send('stream_changed', uri=uri) def _on_playbin_state_changed(self, old_state, new_state, pending_state): if new_state == gst.STATE_READY and pending_state == gst.STATE_NULL: @@ -349,7 +354,7 @@ class Audio(pykka.ThreadingActor): 'state_changed', old_state=old_state, new_state=new_state) def _on_end_of_stream(self): - logger.debug('Triggering reached_end_of_stream event') + logger.debug('Triggering event: reached_end_of_stream event') AudioListener.send('reached_end_of_stream') def set_uri(self, uri): @@ -476,6 +481,22 @@ class Audio(pykka.ThreadingActor): """ return self._set_state(gst.STATE_NULL) + def wait_for_state_change(self): + """Block until any pending state changes are complete. + + Should only be used by test. + """ + self._playbin.get_state() + + def process_messages(self): + """Manually process messages from bus. + + Should only be used by test. + """ + bus = self._playbin.get_bus() + while bus.have_pending(): + self._on_message(bus, bus.pop()) + def _set_state(self, state): """ Internal method for setting the raw GStreamer state. diff --git a/mopidy/audio/listener.py b/mopidy/audio/listener.py index 537a81dd..d2690031 100644 --- a/mopidy/audio/listener.py +++ b/mopidy/audio/listener.py @@ -27,6 +27,16 @@ class AudioListener(listener.Listener): """ pass + def stream_changed(self, uri): + """ + Called whenever the end of the audio stream changes. + + *MAY* be implemented by actor. + + :param string uri: URI the stream has started playing. + """ + pass + def state_changed(self, old_state, new_state): """ Called after the playback state have changed. diff --git a/tests/audio/test_actor.py b/tests/audio/test_actor.py index 3f7e56ce..b0ee4429 100644 --- a/tests/audio/test_actor.py +++ b/tests/audio/test_actor.py @@ -1,5 +1,6 @@ from __future__ import unicode_literals +import mock import unittest import pygst @@ -114,6 +115,18 @@ class AudioTest(unittest.TestCase): def test_invalid_output_raises_error(self): pass # TODO + @mock.patch.object(audio.AudioListener, 'send') + def test_stream_changed_event(self, send_mock): + self.audio.prepare_change() + self.audio.set_uri(self.song_uri) + self.audio.start_playback() + + self.audio.wait_for_state_change() + self.audio.process_messages().get() + + call = mock.call('stream_changed', uri=self.song_uri) + self.assertIn(call, send_mock.call_args_list) + class AudioStateTest(unittest.TestCase): def setUp(self): diff --git a/tests/audio/test_listener.py b/tests/audio/test_listener.py index 08286cf9..c579fd55 100644 --- a/tests/audio/test_listener.py +++ b/tests/audio/test_listener.py @@ -24,3 +24,6 @@ class AudioListenerTest(unittest.TestCase): def test_listener_has_default_impl_for_state_changed(self): self.listener.state_changed(None, None) + + def test_listener_has_default_impl_for_stream_changed(self): + self.listener.stream_changed(None)