diff --git a/docs/changes.rst b/docs/changes.rst index ffb7fbf6..64fe1ad6 100644 --- a/docs/changes.rst +++ b/docs/changes.rst @@ -5,14 +5,30 @@ Changes This change log is used to track all major changes to Mopidy. -v0.9.0 (in development) -======================= - -**Multiple backends support** +v0.9.0 (2012-11-21) +=================== Support for using the local and Spotify backends simultaneously have for a very long time been our most requested feature. Finally, it's here! +**Dependencies** + +- pyspotify >= 1.9, < 1.10 is now required for Spotify support. + +**Documentation** + +- New :ref:`installation` guides, organized by OS and distribution so that you + can follow one concise list of instructions instead of jumping around the + docs to look for instructions for each dependency. + +- Moved :ref:`raspberrypi-installation` howto from the wiki to the docs. + +- Updated :ref:`mpd-clients` overview. + +- Added :ref:`mpris-clients` and :ref:`upnp-clients` overview. + +**Multiple backends support** + - Both the local backend and the Spotify backend are now turned on by default. The local backend is listed first in the :attr:`mopidy.settings.BACKENDS` setting, and are thus given the highest priority in e.g. search results, @@ -24,8 +40,85 @@ long time been our most requested feature. Finally, it's here! As always, see :mod:`mopidy.settings` for the full list of available settings. +**Spotify backend** + +- The Spotify backend now includes release year and artist on albums. + +- :issue:`233`: The Spotify backend now returns the track if you search for the + Spotify track URI. + +- Added support for connecting to the Spotify service through an HTTP or SOCKS + proxy, which is supported by pyspotify >= 1.9. + +- Subscriptions to other Spotify user's "starred" playlists are ignored, as + they currently isn't fully supported by pyspotify. + +**Local backend** + +- :issue:`236`: The ``mopidy-scan`` command failed to include tags from ALAC + files (Apple lossless) because it didn't support multiple tag messages from + GStreamer per track it scanned. + +- Added support for search by filename to local backend. + +**MPD frontend** + +- :issue:`218`: The MPD commands ``listplaylist`` and ``listplaylistinfo`` now + accepts unquoted playlist names if they don't contain spaces. + +- :issue:`246`: The MPD command ``list album artist ""`` and similar + ``search``, ``find``, and ``list`` commands with empty filter values caused a + :exc:`LookupError`, but should have been ignored by the MPD server. + +- The MPD frontend no longer lowercases search queries. This broke e.g. search + by URI, where casing may be essential. + +- The MPD command ``plchanges`` always returned the entire playlist. It now + returns an empty response when the client has seen the latest version. + +- The MPD commands ``search`` and ``find`` now allows the key ``file``, which + is used by ncmpcpp instead of ``filename``. + +- The MPD commands ``search`` and ``find`` now allow search query values to be + empty strings. + +- The MPD command ``listplaylists`` will no longer return playlists without a + name. This could crash ncmpcpp. + +- The MPD command ``list`` will no longer return artist names, album names, or + dates that are blank. + +- The MPD command ``decoders`` will now return an empty response instead of a + "not implemented" error to make the ncmpcpp browse view work the first time + it is opened. + +**MPRIS frontend** + +- The MPRIS playlists interface is now supported by our MPRIS frontend. This + means that you now can select playlists to queue and play from the Ubuntu + Sound Menu. + +**Audio mixers** + +- Made the :mod:`NAD mixer ` responsive to interrupts + during amplifier calibration. It will now quit immediately, while previously + it completed the calibration first, and then quit, which could take more than + 15 seconds. + +**Developer support** + +- Added optional background thread for debugging deadlocks. When the feature is + enabled via the ``--debug-thread`` option or + :attr:`mopidy.settings.DEBUG_THREAD` setting a ``SIGUSR1`` signal will dump + the traceback for all running threads. + +- The settings validator will now allow any setting prefixed with ``CUSTOM_`` + to exist in the settings file. + +**Internal changes** + Internally, Mopidy have seen a lot of changes to pave the way for multiple -backends: +backends and the future HTTP frontend. - A new layer and actor, "core", has been added to our stack, inbetween the frontends and the backends. The responsibility of the core layer and actor is @@ -36,12 +129,6 @@ backends: Frontends no longer know anything about the backends. They just use the :ref:`core-api`. -- The base playback provider has been updated with sane default behavior - instead of empty functions. By default, the playback provider now lets - GStreamer keep track of the current track's time position. The local backend - simply uses the base playback provider without any changes. The same applies - to any future backend that just needs GStreamer to play an URI for it. - - The dependency graph between the core controllers and the backend providers have been straightened out, so that we don't have any circular dependencies. The frontend, core, backend, and audio layers are now strictly separate. The @@ -51,118 +138,113 @@ backends: broadcasting of events to listeners, through e.g. :class:`mopidy.core.CoreListener` and :class:`mopidy.audio.AudioListener`. + See :ref:`concepts` for more details and illustrations of all the relations. + - All dependencies are now explicitly passed to the constructors of the frontends, core, and the backends. This makes testing each layer with dummy/mocked lower layers easier than with the old variant, where dependencies where looked up in Pykka's actor registry. -- Renamed "current playlist" to "tracklist" everywhere, including the core API - used by frontends. +- All properties in the core API now got getters, and setters if setting them + is allowed. They are not explictly listed in the docs as they have the same + behavior as the documented properties, but they are available and may be + used. This is useful for the future HTTP frontend. -- Renamed "stored playlists" to "playlists" everywhere, including the core API - used by frontends. - -- The playlists part of the core API has been revised to be more focused around - the playlist URI, and some redundant functionality has been removed: - - - :attr:`mopidy.core.PlaylistsController.playlists` no longer supports - assignment to it. The `playlists` property on the backend layer still does, - and all functionality is maintained by assigning to the playlists - collections at the backend level. - - - :meth:`mopidy.core.PlaylistsController.delete` now accepts an URI, and not - a playlist object. - - - :meth:`mopidy.core.PlaylistsController.save` now returns the saved - playlist. The returned playlist may differ from the saved playlist, and - should thus be used instead of the playlist passed to ``save()``. - - - :meth:`mopidy.core.PlaylistsController.rename` has been removed, since - renaming can be done with ``save()``. - -**Changes** - -- Made the :mod:`NAD mixer ` responsive to interrupts - during amplifier calibration. It will now quit immediately, while previously - it completed the calibration first, and then quit, which could take more than - 15 seconds. +*Models:* - Added :attr:`mopidy.models.Album.date` attribute. It has the same format as the existing :attr:`mopidy.models.Track.date`. -- The Spotify backend now includes release year and artist on albums. +- Added :class:`mopidy.models.ModelJSONEncoder` and + :func:`mopidy.models.model_json_decoder` for automatic JSON serialization and + deserialization of data structures which contains Mopidy models. This is + useful for the future HTTP frontend. -- Added support for search by filename to local backend. +*Library:* -- Added optional background thread for debugging deadlocks. When the feature is - enabled via the ``--debug-thread`` option or - :attr:`mopidy.settings.DEBUG_THREAD` setting a ``SIGUSR1`` signal will dump - the traceback for all running threads. +- :meth:`mopidy.core.LibraryController.find_exact` and + :meth:`mopidy.core.LibraryController.search` now returns plain lists of + tracks instead of playlist objects. -- Make the entire code base use unicode strings by default, and only fall back - to bytestrings where it is required. Another step closer to Python 3. +- :meth:`mopidy.core.LibraryController.lookup` now returns a list of tracks + instead of a single track. This makes it possible to support lookup of + artist or album URIs which then can expand to a list of tracks. -- The settings validator will now allow any setting prefixed with ``CUSTOM_`` - to exist in the settings file. +*Playback:* -- The MPD commands ``search`` and ``find`` now allows the key ``file``, which - is used by ncmpcpp instead of ``filename``. +- The base playback provider has been updated with sane default behavior + instead of empty functions. By default, the playback provider now lets + GStreamer keep track of the current track's time position. The local backend + simply uses the base playback provider without any changes. Any future + backend that just feeds URIs to GStreamer to play can also use the base + playback provider without any changes. -- The Spotify backend now returns the track if you search for the Spotify track - URI. (Fixes: :issue:`233`) +- Removed :attr:`mopidy.core.PlaybackController.track_at_previous`. Use + :attr:`mopidy.core.PlaybackController.tl_track_at_previous` instead. -- :meth:`mopidy.core.TracklistController.append` now returns a list of the - :class:`mopidy.models.TlTrack` instances that was added to the tracklist. - This makes it easier to start playing one of the tracks that was just - appended to the tracklist. +- Removed :attr:`mopidy.core.PlaybackController.track_at_next`. Use + :attr:`mopidy.core.PlaybackController.tl_track_at_next` instead. + +- Removed :attr:`mopidy.core.PlaybackController.track_at_eot`. Use + :attr:`mopidy.core.PlaybackController.tl_track_at_eot` instead. + +- Removed :attr:`mopidy.core.PlaybackController.current_tlid`. Use + :attr:`mopidy.core.PlaybackController.current_tl_track` instead. + +*Playlists:* + +The playlists part of the core API has been revised to be more focused around +the playlist URI, and some redundant functionality has been removed: + +- Renamed "stored playlists" to "playlists" everywhere, including the core API + used by frontends. + +- :attr:`mopidy.core.PlaylistsController.playlists` no longer supports + assignment to it. The `playlists` property on the backend layer still does, + and all functionality is maintained by assigning to the playlists collections + at the backend level. + +- :meth:`mopidy.core.PlaylistsController.delete` now accepts an URI, and not a + playlist object. + +- :meth:`mopidy.core.PlaylistsController.save` now returns the saved playlist. + The returned playlist may differ from the saved playlist, and should thus be + used instead of the playlist passed to + :meth:`mopidy.core.PlaylistsController.save`. + +- :meth:`mopidy.core.PlaylistsController.rename` has been removed, since + renaming can be done with :meth:`mopidy.core.PlaylistsController.save`. + +- :meth:`mopidy.core.PlaylistsController.get` has been replaced by + :meth:`mopidy.core.PlaylistsController.filter`. + +- The event :meth:`mopidy.core.CoreListener.playlist_changed` has been changed + to include the playlist that was changed. + +*Tracklist:* + +- Renamed "current playlist" to "tracklist" everywhere, including the core API + used by frontends. + +- Removed :meth:`mopidy.core.TracklistController.append`. Use + :meth:`mopidy.core.TracklistController.add` instead, which is now capable of + adding multiple tracks. + +- :meth:`mopidy.core.TracklistController.get` has been replaced by + :meth:`mopidy.core.TracklistController.filter`. + +- :meth:`mopidy.core.TracklistController.remove` can now remove multiple + tracks, and returns the tracks it removed. - When the tracklist is changed, we now trigger the new :meth:`mopidy.core.CoreListener.tracklist_changed` event. Previously we triggered :meth:`mopidy.core.CoreListener.playlist_changed`, which is intended for stored playlists, not the tracklist. -- The event :meth:`mopidy.core.CoreListener.playlist_changed` has been changed - to include the playlist that was changed. +*Towards Python 3 support:* -- The MPRIS playlists interface is now supported by our MPRIS frontend. This - means that you now can select playlists to queue and play from the Ubuntu - Sound Menu. - -- :meth:`mopidy.core.LibraryController.find_exact` and - :meth:`mopidy.core.LibraryController.search` now returns plain lists of - tracks instead of playlist objects. - -- :meth:`mopidy.core.TracklistController.get` has been replaced by - :meth:`mopidy.core.TracklistController.filter`. - -- :meth:`mopidy.core.PlaylistsController.get` has been replaced by - :meth:`mopidy.core.PlaylistsController.filter`. - -- :meth:`mopidy.core.TracklistController.remove` can now remove multiple - tracks, and returns the tracks it removed. - -- :meth:`mopidy.core.LibraryController.lookup` now returns a list of tracks. - This makes it possible to support lookup of artist or album URIs which then - can expand to a list of tracks. - -**Bug fixes** - -- :issue:`218`: The MPD commands ``listplaylist`` and ``listplaylistinfo`` now - accepts unquotes playlist names if they don't contain spaces. - -- The MPD command ``plchanges`` always returned the entire playlist. It now - returns an empty response when the client has seen the latest version. - -- MPD no longer lowercases search queries. This broke e.g. search by URI, where - casing may be essential. - -- :issue:`236`: The ``mopidy-scan`` command failed to include tags from ALAC - files (Apple lossless) because it didn't support multiple tag messages from - GStreamer per track it scanned. - -- :issue:`246`: The MPD command ``list album artist ""`` and similar - ``search``, ``find``, and ``list`` commands with empty filter values caused a - :exc:`LookupError`, but should have been ignored by the MPD server. +- Make the entire code base use unicode strings by default, and only fall back + to bytestrings where it is required. Another step closer to Python 3. v0.8.1 (2012-10-30) diff --git a/mopidy/__init__.py b/mopidy/__init__.py index 072a604c..918e1459 100644 --- a/mopidy/__init__.py +++ b/mopidy/__init__.py @@ -23,7 +23,7 @@ if (isinstance(pykka.__version__, basestring) warnings.filterwarnings('ignore', 'could not open display') -__version__ = '0.8.1' +__version__ = '0.9.0' from mopidy import settings as default_settings_module diff --git a/mopidy/backends/dummy.py b/mopidy/backends/dummy.py index 62ac8e8f..39180bbb 100644 --- a/mopidy/backends/dummy.py +++ b/mopidy/backends/dummy.py @@ -37,9 +37,11 @@ class DummyLibraryProvider(base.BaseLibraryProvider): def __init__(self, *args, **kwargs): super(DummyLibraryProvider, self).__init__(*args, **kwargs) self.dummy_library = [] + self.dummy_find_exact_result = [] + self.dummy_search_result = [] def find_exact(self, **query): - return [] + return self.dummy_find_exact_result def lookup(self, uri): return filter(lambda t: uri == t.uri, self.dummy_library) @@ -48,7 +50,7 @@ class DummyLibraryProvider(base.BaseLibraryProvider): pass def search(self, **query): - return [] + return self.dummy_search_result class DummyPlaybackProvider(base.BasePlaybackProvider): diff --git a/mopidy/backends/spotify/__init__.py b/mopidy/backends/spotify/__init__.py index fa6feb99..141656cc 100644 --- a/mopidy/backends/spotify/__init__.py +++ b/mopidy/backends/spotify/__init__.py @@ -21,7 +21,7 @@ https://github.com/mopidy/mopidy/issues?labels=Spotify+backend **Dependencies:** - libspotify >= 12, < 13 (libspotify12 package from apt.mopidy.com) -- pyspotify >= 1.8, < 1.9 (python-spotify package from apt.mopidy.com) +- pyspotify >= 1.9, < 1.10 (python-spotify package from apt.mopidy.com) **Settings:** diff --git a/mopidy/backends/spotify/actor.py b/mopidy/backends/spotify/actor.py index 5fc5cc4f..5e90205b 100644 --- a/mopidy/backends/spotify/actor.py +++ b/mopidy/backends/spotify/actor.py @@ -31,9 +31,14 @@ class SpotifyBackend(pykka.ThreadingActor, base.Backend): # Fail early if settings are not present username = settings.SPOTIFY_USERNAME password = settings.SPOTIFY_PASSWORD + proxy = settings.SPOTIFY_PROXY_HOST + proxy_username = settings.SPOTIFY_PROXY_USERNAME + proxy_password = settings.SPOTIFY_PROXY_PASSWORD self.spotify = SpotifySessionManager( - username, password, audio=audio, backend_ref=self.actor_ref) + username, password, audio=audio, backend_ref=self.actor_ref, + proxy=proxy, proxy_username=proxy_username, + proxy_password=proxy_password) def on_start(self): logger.info('Mopidy uses SPOTIFY(R) CORE') diff --git a/mopidy/backends/spotify/playback.py b/mopidy/backends/spotify/playback.py index d3585021..e4534172 100644 --- a/mopidy/backends/spotify/playback.py +++ b/mopidy/backends/spotify/playback.py @@ -46,10 +46,11 @@ class SpotifyPlaybackProvider(base.BasePlaybackProvider): def resume(self): time_position = self.get_time_position() - self._timer.resume() - - return self.seek(time_position) + self.audio.prepare_change() + result = self.seek(time_position) + self.audio.start_playback() + return result def seek(self, time_position): self.backend.spotify.session.seek(time_position) diff --git a/mopidy/backends/spotify/session_manager.py b/mopidy/backends/spotify/session_manager.py index 821bd27c..cfe4e433 100644 --- a/mopidy/backends/spotify/session_manager.py +++ b/mopidy/backends/spotify/session_manager.py @@ -10,7 +10,7 @@ import threading from spotify.manager import SpotifySessionManager as PyspotifySessionManager -from mopidy import settings +from mopidy import audio, settings from mopidy.backends.listener import BackendListener from mopidy.utils import process, versioning @@ -32,8 +32,12 @@ class SpotifySessionManager(process.BaseThread, PyspotifySessionManager): appkey_file = os.path.join(os.path.dirname(__file__), 'spotify_appkey.key') user_agent = 'Mopidy %s' % versioning.get_version() - def __init__(self, username, password, audio, backend_ref): - PyspotifySessionManager.__init__(self, username, password) + def __init__(self, username, password, audio, backend_ref, proxy=None, + proxy_username=None, proxy_password=None): + PyspotifySessionManager.__init__( + self, username, password, proxy=proxy, + proxy_username=proxy_username, + proxy_password=proxy_password) process.BaseThread.__init__(self) self.name = 'SpotifyThread' @@ -88,7 +92,8 @@ class SpotifySessionManager(process.BaseThread, PyspotifySessionManager): logger.info('Spotify connection OK') else: logger.error('Spotify connection error: %s', error) - self.backend.playback.pause() + if self.audio.state.get() == audio.PlaybackState.PLAYING: + self.backend.playback.pause() def message_to_user(self, session, message): """Callback used by pyspotify""" diff --git a/mopidy/backends/spotify/translator.py b/mopidy/backends/spotify/translator.py index 834b34d8..92b4514e 100644 --- a/mopidy/backends/spotify/translator.py +++ b/mopidy/backends/spotify/translator.py @@ -56,6 +56,10 @@ def to_mopidy_playlist(spotify_playlist): uri = str(Link.from_playlist(spotify_playlist)) if not spotify_playlist.is_loaded(): return Playlist(uri=uri, name='[loading...]') + if not spotify_playlist.name(): + # Other user's "starred" playlists isn't handled properly by pyspotify + # See https://github.com/mopidy/pyspotify/issues/81 + return return Playlist( uri=uri, name=spotify_playlist.name(), diff --git a/mopidy/core/actor.py b/mopidy/core/actor.py index a4f184bf..cd4ba180 100644 --- a/mopidy/core/actor.py +++ b/mopidy/core/actor.py @@ -46,14 +46,15 @@ class Core(pykka.ThreadingActor, AudioListener, BackendListener): self.tracklist = TracklistController(core=self) - @property - def uri_schemes(self): - """List of URI schemes we can handle""" + def get_uri_schemes(self): futures = [b.uri_schemes for b in self.backends] results = pykka.get_all(futures) uri_schemes = itertools.chain(*results) return sorted(uri_schemes) + uri_schemes = property(get_uri_schemes) + """List of URI schemes we can handle""" + def reached_end_of_stream(self): self.playback.on_end_of_track() diff --git a/mopidy/core/playback.py b/mopidy/core/playback.py index 94b4af9c..e50de2e7 100644 --- a/mopidy/core/playback.py +++ b/mopidy/core/playback.py @@ -12,56 +12,12 @@ from . import listener logger = logging.getLogger('mopidy.core') -def option_wrapper(name, default): - def get_option(self): - return getattr(self, name, default) - - def set_option(self, value): - if getattr(self, name, default) != value: - # pylint: disable = W0212 - self._trigger_options_changed() - # pylint: enable = W0212 - return setattr(self, name, value) - - return property(get_option, set_option) - - class PlaybackController(object): # pylint: disable = R0902 # Too many instance attributes pykka_traversable = True - #: :class:`True` - #: Tracks are removed from the playlist when they have been played. - #: :class:`False` - #: Tracks are not removed from the playlist. - consume = option_wrapper('_consume', False) - - #: The currently playing or selected :class:`mopidy.models.TlTrack`, or - #: :class:`None`. - current_tl_track = None - - #: :class:`True` - #: Tracks are selected at random from the playlist. - #: :class:`False` - #: Tracks are played in the order of the playlist. - random = option_wrapper('_random', False) - - #: :class:`True` - #: The current playlist is played repeatedly. To repeat a single track, - #: select both :attr:`repeat` and :attr:`single`. - #: :class:`False` - #: The current playlist is played once. - repeat = option_wrapper('_repeat', False) - - #: :class:`True` - #: Playback is stopped after current song, unless in :attr:`repeat` - #: mode. - #: :class:`False` - #: Playback continues after current song. - single = option_wrapper('_single', False) - def __init__(self, audio, backends, core): self.audio = audio self.backends = backends @@ -79,42 +35,128 @@ class PlaybackController(object): uri_scheme = urlparse.urlparse(uri).scheme return self.backends.with_playback_by_uri_scheme.get(uri_scheme, None) - def _get_tlid(self, tl_track): - if tl_track is None: - return None - return tl_track.tlid + ### Properties - def _get_track(self, tl_track): - if tl_track is None: - return None - return tl_track.track + def get_consume(self): + return getattr(self, '_consume', False) - @property - def current_tlid(self): - """ - The TLID (tracklist ID) of the currently playing or selected - track. + def set_consume(self, value): + if self.get_consume() != value: + self._trigger_options_changed() + return setattr(self, '_consume', value) - Read-only. Extracted from :attr:`current_tl_track` for convenience. - """ - return self._get_tlid(self.current_tl_track) + consume = property(get_consume, set_consume) + """ + :class:`True` + Tracks are removed from the playlist when they have been played. + :class:`False` + Tracks are not removed from the playlist. + """ - @property - def current_track(self): - """ - The currently playing or selected :class:`mopidy.models.Track`. + current_tl_track = None + """ + The currently playing or selected :class:`mopidy.models.TlTrack`, or + :class:`None`. + """ - Read-only. Extracted from :attr:`current_tl_track` for convenience. - """ - return self._get_track(self.current_tl_track) + def get_current_track(self): + return self.current_tl_track and self.current_tl_track.track - @property - def tracklist_position(self): - """ - The position of the current track in the tracklist. + current_track = property(get_current_track) + """ + The currently playing or selected :class:`mopidy.models.Track`. - Read-only. - """ + Read-only. Extracted from :attr:`current_tl_track` for convenience. + """ + + def get_random(self): + return getattr(self, '_random', False) + + def set_random(self, value): + if self.get_random() != value: + self._trigger_options_changed() + return setattr(self, '_random', value) + + random = property(get_random, set_random) + """ + :class:`True` + Tracks are selected at random from the playlist. + :class:`False` + Tracks are played in the order of the playlist. + """ + + def get_repeat(self): + return getattr(self, '_repeat', False) + + def set_repeat(self, value): + if self.get_repeat() != value: + self._trigger_options_changed() + return setattr(self, '_repeat', value) + + repeat = property(get_repeat, set_repeat) + """ + :class:`True` + The current playlist is played repeatedly. To repeat a single track, + select both :attr:`repeat` and :attr:`single`. + :class:`False` + The current playlist is played once. + """ + + def get_single(self): + return getattr(self, '_single', False) + + def set_single(self, value): + if self.get_single() != value: + self._trigger_options_changed() + return setattr(self, '_single', value) + + single = property(get_single, set_single) + """ + :class:`True` + Playback is stopped after current song, unless in :attr:`repeat` + mode. + :class:`False` + Playback continues after current song. + """ + + def get_state(self): + return self._state + + def set_state(self, new_state): + (old_state, self._state) = (self.state, new_state) + logger.debug('Changing state: %s -> %s', old_state, new_state) + + self._trigger_playback_state_changed(old_state, new_state) + + state = property(get_state, set_state) + """ + The playback state. Must be :attr:`PLAYING`, :attr:`PAUSED`, or + :attr:`STOPPED`. + + Possible states and transitions: + + .. digraph:: state_transitions + + "STOPPED" -> "PLAYING" [ label="play" ] + "STOPPED" -> "PAUSED" [ label="pause" ] + "PLAYING" -> "STOPPED" [ label="stop" ] + "PLAYING" -> "PAUSED" [ label="pause" ] + "PLAYING" -> "PLAYING" [ label="play" ] + "PAUSED" -> "PLAYING" [ label="resume" ] + "PAUSED" -> "STOPPED" [ label="stop" ] + """ + + def get_time_position(self): + backend = self._get_backend() + if backend: + return backend.playback.get_time_position().get() + else: + return 0 + + time_position = property(get_time_position) + """Time position in milliseconds.""" + + def get_tracklist_position(self): if self.current_tl_track is None: return None try: @@ -122,25 +164,14 @@ class PlaybackController(object): except ValueError: return None - @property - def track_at_eot(self): - """ - The track that will be played at the end of the current track. + tracklist_position = property(get_tracklist_position) + """ + The position of the current track in the tracklist. - Read-only. A :class:`mopidy.models.Track` extracted from - :attr:`tl_track_at_eot` for convenience. - """ - return self._get_track(self.tl_track_at_eot) + Read-only. + """ - @property - def tl_track_at_eot(self): - """ - The track that will be played at the end of the current track. - - Read-only. A :class:`mopidy.models.TlTrack`. - - Not necessarily the same track as :attr:`tl_track_at_next`. - """ + def get_tl_track_at_eot(self): # pylint: disable = R0911 # Too many return statements @@ -173,28 +204,16 @@ class PlaybackController(object): except IndexError: return None - @property - def track_at_next(self): - """ - The track that will be played if calling :meth:`next()`. + tl_track_at_eot = property(get_tl_track_at_eot) + """ + The track that will be played at the end of the current track. - Read-only. A :class:`mopidy.models.Track` extracted from - :attr:`tl_track_at_next` for convenience. - """ - return self._get_track(self.tl_track_at_next) + Read-only. A :class:`mopidy.models.TlTrack`. - @property - def tl_track_at_next(self): - """ - The track that will be played if calling :meth:`next()`. + Not necessarily the same track as :attr:`tl_track_at_next`. + """ - Read-only. A :class:`mopidy.models.TlTrack`. - - For normal playback this is the next track in the playlist. If repeat - is enabled the next track can loop around the playlist. When random is - enabled this should be a random track, all tracks should be played once - before the list repeats. - """ + def get_tl_track_at_next(self): tl_tracks = self.core.tracklist.tl_tracks if not tl_tracks: @@ -221,27 +240,19 @@ class PlaybackController(object): except IndexError: return None - @property - def track_at_previous(self): - """ - The track that will be played if calling :meth:`previous()`. + tl_track_at_next = property(get_tl_track_at_next) + """ + The track that will be played if calling :meth:`next()`. - Read-only. A :class:`mopidy.models.Track` extracted from - :attr:`tl_track_at_previous` for convenience. - """ - return self._get_track(self.tl_track_at_previous) + Read-only. A :class:`mopidy.models.TlTrack`. - @property - def tl_track_at_previous(self): - """ - The track that will be played if calling :meth:`previous()`. + For normal playback this is the next track in the playlist. If repeat + is enabled the next track can loop around the playlist. When random is + enabled this should be a random track, all tracks should be played once + before the list repeats. + """ - A :class:`mopidy.models.TlTrack`. - - For normal playback this is the previous track in the playlist. If - random and/or consume is enabled it should return the current track - instead. - """ + def get_tl_track_at_previous(self): if self.repeat or self.consume or self.random: return self.current_tl_track @@ -250,59 +261,36 @@ class PlaybackController(object): return self.core.tracklist.tl_tracks[self.tracklist_position - 1] - @property - def state(self): - """ - The playback state. Must be :attr:`PLAYING`, :attr:`PAUSED`, or - :attr:`STOPPED`. + tl_track_at_previous = property(get_tl_track_at_previous) + """ + The track that will be played if calling :meth:`previous()`. - Possible states and transitions: + A :class:`mopidy.models.TlTrack`. - .. digraph:: state_transitions + For normal playback this is the previous track in the playlist. If + random and/or consume is enabled it should return the current track + instead. + """ - "STOPPED" -> "PLAYING" [ label="play" ] - "STOPPED" -> "PAUSED" [ label="pause" ] - "PLAYING" -> "STOPPED" [ label="stop" ] - "PLAYING" -> "PAUSED" [ label="pause" ] - "PLAYING" -> "PLAYING" [ label="play" ] - "PAUSED" -> "PLAYING" [ label="resume" ] - "PAUSED" -> "STOPPED" [ label="stop" ] - """ - return self._state - - @state.setter # noqa - def state(self, new_state): - (old_state, self._state) = (self.state, new_state) - logger.debug('Changing state: %s -> %s', old_state, new_state) - - self._trigger_playback_state_changed(old_state, new_state) - - @property - def time_position(self): - """Time position in milliseconds.""" - backend = self._get_backend() - if backend: - return backend.playback.get_time_position().get() - else: - return 0 - - @property - def volume(self): - """Volume as int in range [0..100] or :class:`None`""" + def get_volume(self): if self.audio: return self.audio.get_volume().get() else: # For testing return self._volume - @volume.setter # noqa - def volume(self, volume): + def set_volume(self, volume): if self.audio: self.audio.set_volume(volume) else: # For testing self._volume = volume + volume = property(get_volume, set_volume) + """Volume as int in range [0..100] or :class:`None`""" + + ### Methods + def change_track(self, tl_track, on_error_step=1): """ Change to the given track, keeping the current playback state. @@ -324,6 +312,8 @@ class PlaybackController(object): def on_end_of_track(self): """ Tell the playback controller that end of track is reached. + + Used by event handler in :class:`mopidy.core.Core`. """ if self.state == PlaybackState.STOPPED: return @@ -343,7 +333,7 @@ class PlaybackController(object): """ Tell the playback controller that the current playlist has changed. - Used by :class:`mopidy.core.CurrentPlaylistController`. + Used by :class:`mopidy.core.TracklistController`. """ self._first_shuffle = True self._shuffled = [] diff --git a/mopidy/core/playlists.py b/mopidy/core/playlists.py index dcdc665f..6a368ac6 100644 --- a/mopidy/core/playlists.py +++ b/mopidy/core/playlists.py @@ -15,18 +15,19 @@ class PlaylistsController(object): self.backends = backends self.core = core - @property - def playlists(self): - """ - The available playlists. - - Read-only. List of :class:`mopidy.models.Playlist`. - """ + def get_playlists(self): futures = [ b.playlists.playlists for b in self.backends.with_playlists] results = pykka.get_all(futures) return list(itertools.chain(*results)) + playlists = property(get_playlists) + """ + The available playlists. + + Read-only. List of :class:`mopidy.models.Playlist`. + """ + def create(self, name, uri_scheme=None): """ Create a new playlist. diff --git a/mopidy/core/tracklist.py b/mopidy/core/tracklist.py index e00a42f9..656e15b1 100644 --- a/mopidy/core/tracklist.py +++ b/mopidy/core/tracklist.py @@ -20,89 +20,77 @@ class TracklistController(object): self._tl_tracks = [] self._version = 0 - @property - def tl_tracks(self): - """ - List of :class:`mopidy.models.TlTrack`. - - Read-only. - """ + def get_tl_tracks(self): return self._tl_tracks[:] - @property - def tracks(self): - """ - List of :class:`mopidy.models.Track` in the tracklist. + tl_tracks = property(get_tl_tracks) + """ + List of :class:`mopidy.models.TlTrack`. - Read-only. - """ + Read-only. + """ + + def get_tracks(self): return [tl_track.track for tl_track in self._tl_tracks] - @property - def length(self): - """ - Length of the tracklist. - """ + tracks = property(get_tracks) + """ + List of :class:`mopidy.models.Track` in the tracklist. + + Read-only. + """ + + def get_length(self): return len(self._tl_tracks) - @property - def version(self): - """ - The tracklist version. Integer which is increased every time the - tracklist is changed. Is not reset before Mopidy is restarted. - """ + length = property(get_length) + """Length of the tracklist.""" + + def get_version(self): return self._version - @version.setter # noqa - def version(self, version): - self._version = version + def _increase_version(self): + self._version += 1 self._core.playback.on_tracklist_change() self._trigger_tracklist_changed() - def add(self, track, at_position=None, increase_version=True): + version = property(get_version) + """ + The tracklist version. + + Read-only. Integer which is increased every time the tracklist is changed. + Is not reset before Mopidy is restarted. + """ + + def add(self, tracks, at_position=None): """ - Add the track to the end of, or at the given position in the tracklist. + Add the track or list of tracks to the tracklist. - Triggers the :method:`mopidy.core.CoreListener.tracklist_changed` - event. + If ``at_position`` is given, the tracks placed at the given position in + the tracklist. If ``at_position`` is not given, the tracks are appended + to the end of the tracklist. - :param track: track to add - :type track: :class:`mopidy.models.Track` + Triggers the :meth:`mopidy.core.CoreListener.tracklist_changed` event. + + :param tracks: tracks to add + :type tracks: list of :class:`mopidy.models.Track` :param at_position: position in tracklist to add track :type at_position: int or :class:`None` - :param increase_version: if the tracklist version should be increased - :type increase_version: :class:`True` or :class:`False` - :rtype: :class:`mopidy.models.TlTrack` that was added to the tracklist - """ - assert at_position <= len(self._tl_tracks), \ - 'at_position can not be greater than tracklist length' - tl_track = TlTrack(self._next_tlid, track) - if at_position is not None: - self._tl_tracks.insert(at_position, tl_track) - else: - self._tl_tracks.append(tl_track) - if increase_version: - self.version += 1 - self._next_tlid += 1 - return tl_track - - def append(self, tracks): - """ - Append the given tracks to the tracklist. - - Triggers the :method:`mopidy.core.CoreListener.tracklist_changed` - event. - - :param tracks: tracks to append - :type tracks: list of :class:`mopidy.models.Track` - :rtype: list of class:`mopidy.models.TlTrack` + :rtype: list of :class:`mopidy.models.TlTrack` """ tl_tracks = [] for track in tracks: - tl_tracks.append(self.add(track, increase_version=False)) + tl_track = TlTrack(self._next_tlid, track) + self._next_tlid += 1 + if at_position is not None: + self._tl_tracks.insert(at_position, tl_track) + at_position += 1 + else: + self._tl_tracks.append(tl_track) + tl_tracks.append(tl_track) - if tracks: - self.version += 1 + if tl_tracks: + self._increase_version() return tl_tracks @@ -110,11 +98,10 @@ class TracklistController(object): """ Clear the tracklist. - Triggers the :method:`mopidy.core.CoreListener.tracklist_changed` - event. + Triggers the :meth:`mopidy.core.CoreListener.tracklist_changed` event. """ self._tl_tracks = [] - self.version += 1 + self._increase_version() def filter(self, **criteria): """ @@ -156,8 +143,7 @@ class TracklistController(object): """ Move the tracks in the slice ``[start:end]`` to ``to_position``. - Triggers the :method:`mopidy.core.CoreListener.tracklist_changed` - event. + Triggers the :meth:`mopidy.core.CoreListener.tracklist_changed` event. :param start: position of first track to move :type start: int @@ -184,7 +170,7 @@ class TracklistController(object): new_tl_tracks.insert(to_position, tl_track) to_position += 1 self._tl_tracks = new_tl_tracks - self.version += 1 + self._increase_version() def remove(self, **criteria): """ @@ -192,8 +178,7 @@ class TracklistController(object): Uses :meth:`filter()` to lookup the tracks to remove. - Triggers the :method:`mopidy.core.CoreListener.tracklist_changed` - event. + Triggers the :meth:`mopidy.core.CoreListener.tracklist_changed` event. :param criteria: on or more criteria to match by :type criteria: dict @@ -203,7 +188,7 @@ class TracklistController(object): for tl_track in tl_tracks: position = self._tl_tracks.index(tl_track) del self._tl_tracks[position] - self.version += 1 + self._increase_version() return tl_tracks def shuffle(self, start=None, end=None): @@ -211,8 +196,7 @@ class TracklistController(object): Shuffles the entire tracklist. If ``start`` and ``end`` is given only shuffles the slice ``[start:end]``. - Triggers the :method:`mopidy.core.CoreListener.tracklist_changed` - event. + Triggers the :meth:`mopidy.core.CoreListener.tracklist_changed` event. :param start: position of first track to shuffle :type start: int or :class:`None` @@ -236,7 +220,7 @@ class TracklistController(object): after = tl_tracks[end or len(tl_tracks):] random.shuffle(shuffled) self._tl_tracks = before + shuffled + after - self.version += 1 + self._increase_version() def slice(self, start, end): """ diff --git a/mopidy/frontends/mpd/protocol/__init__.py b/mopidy/frontends/mpd/protocol/__init__.py index 3a9f3674..a8bdc2c7 100644 --- a/mopidy/frontends/mpd/protocol/__init__.py +++ b/mopidy/frontends/mpd/protocol/__init__.py @@ -56,10 +56,11 @@ def handle_request(pattern, auth_required=True): if match is not None: mpd_commands.add( MpdCommand(name=match.group(), auth_required=auth_required)) - if pattern in request_handlers: + compiled_pattern = re.compile(pattern, flags=re.UNICODE) + if compiled_pattern in request_handlers: raise ValueError('Tried to redefine handler for %s with %s' % ( pattern, func)) - request_handlers[pattern] = func + request_handlers[compiled_pattern] = func func.__doc__ = ' - *Pattern:* ``%s``\n\n%s' % ( pattern, func.__doc__ or '') return func diff --git a/mopidy/frontends/mpd/protocol/current_playlist.py b/mopidy/frontends/mpd/protocol/current_playlist.py index da950078..d1b0e59a 100644 --- a/mopidy/frontends/mpd/protocol/current_playlist.py +++ b/mopidy/frontends/mpd/protocol/current_playlist.py @@ -24,7 +24,7 @@ def add(context, uri): return tracks = context.core.library.lookup(uri).get() if tracks: - context.core.tracklist.append(tracks) + context.core.tracklist.add(tracks) return raise MpdNoExistError('directory or file not found', command='add') @@ -57,14 +57,8 @@ def addid(context, uri, songpos=None): raise MpdNoExistError('No such song', command='addid') if songpos and songpos > context.core.tracklist.length.get(): raise MpdArgError('Bad song index', command='addid') - first_tl_track = None - for track in tracks: - tl_track = context.core.tracklist.add(track, at_position=songpos).get() - if songpos is not None: - songpos += 1 - if first_tl_track is None: - first_tl_track = tl_track - return ('Id', first_tl_track.tlid) + tl_tracks = context.core.tracklist.add(tracks, at_position=songpos).get() + return ('Id', tl_tracks[0].tlid) @handle_request(r'^delete "(?P\d+):(?P\d+)*"$') @@ -110,7 +104,8 @@ def deleteid(context, tlid): Deletes the song ``SONGID`` from the playlist """ tlid = int(tlid) - if context.core.playback.current_tlid.get() == tlid: + tl_track = context.core.playback.current_tl_track.get() + if tl_track and tl_track.tlid == tlid: context.core.playback.next() tl_tracks = context.core.tracklist.remove(tlid=tlid).get() if not tl_tracks: @@ -237,7 +232,6 @@ def playlistid(context, tlid=None): @handle_request(r'^playlistinfo$') -@handle_request(r'^playlistinfo "-1"$') @handle_request(r'^playlistinfo "(?P-?\d+)"$') @handle_request(r'^playlistinfo "(?P\d+):(?P\d+)*"$') def playlistinfo(context, songpos=None, start=None, end=None): @@ -255,6 +249,8 @@ def playlistinfo(context, songpos=None, start=None, end=None): - uses negative indexes, like ``playlistinfo "-1"``, to request the entire playlist """ + if songpos == '-1': + songpos = None if songpos is not None: songpos = int(songpos) tl_track = context.core.tracklist.tl_tracks.get()[songpos] @@ -274,7 +270,7 @@ def playlistinfo(context, songpos=None, start=None, end=None): @handle_request(r'^playlistsearch "(?P[^"]+)" "(?P[^"]+)"$') -@handle_request(r'^playlistsearch (?P\S+) "(?P[^"]+)"$') +@handle_request(r'^playlistsearch (?P\w+) "(?P[^"]+)"$') def playlistsearch(context, tag, needle): """ *musicpd.org, current playlist section:* @@ -376,7 +372,7 @@ def swap(context, songpos1, songpos2): del tracks[songpos2] tracks.insert(songpos2, song1) context.core.tracklist.clear() - context.core.tracklist.append(tracks) + context.core.tracklist.add(tracks) @handle_request(r'^swapid "(?P\d+)" "(?P\d+)"$') diff --git a/mopidy/frontends/mpd/protocol/music_db.py b/mopidy/frontends/mpd/protocol/music_db.py index 4d6433f1..00b9ec00 100644 --- a/mopidy/frontends/mpd/protocol/music_db.py +++ b/mopidy/frontends/mpd/protocol/music_db.py @@ -52,7 +52,7 @@ def count(context, tag, needle): @handle_request( r'^find (?P("?([Aa]lbum|[Aa]rtist|[Dd]ate|[Ff]ile[name]*|' - r'[Tt]itle|[Aa]ny)"? "[^"]+"\s?)+)$') + r'[Tt]itle|[Aa]ny)"? "[^"]*"\s?)+)$') def find(context, mpd_query): """ *musicpd.org, music database section:* @@ -250,7 +250,8 @@ def _list_artist(context, query): tracks = context.core.library.find_exact(**query).get() for track in tracks: for artist in track.artists: - artists.add(('Artist', artist.name)) + if artist.name: + artists.add(('Artist', artist.name)) return artists @@ -258,7 +259,7 @@ def _list_album(context, query): albums = set() tracks = context.core.library.find_exact(**query).get() for track in tracks: - if track.album is not None: + if track.album and track.album.name: albums.add(('Album', track.album.name)) return albums @@ -267,7 +268,7 @@ def _list_date(context, query): dates = set() tracks = context.core.library.find_exact(**query).get() for track in tracks: - if track.date is not None: + if track.date: dates.add(('Date', track.date)) return dates @@ -334,7 +335,7 @@ def rescan(context, uri=None): @handle_request( r'^search (?P("?([Aa]lbum|[Aa]rtist|[Dd]ate|[Ff]ile[name]*|' - r'[Tt]itle|[Aa]ny)"? "[^"]+"\s?)+)$') + r'[Tt]itle|[Aa]ny)"? "[^"]*"\s?)+)$') def search(context, mpd_query): """ *musicpd.org, music database section:* diff --git a/mopidy/frontends/mpd/protocol/playback.py b/mopidy/frontends/mpd/protocol/playback.py index d166f982..5a4569e1 100644 --- a/mopidy/frontends/mpd/protocol/playback.py +++ b/mopidy/frontends/mpd/protocol/playback.py @@ -329,9 +329,9 @@ def seek(context, songpos, seconds): - issues ``seek 1 120`` without quotes around the arguments. """ - if context.core.playback.tracklist_position != songpos: + if context.core.playback.tracklist_position.get() != songpos: playpos(context, songpos) - context.core.playback.seek(int(seconds) * 1000) + context.core.playback.seek(int(seconds) * 1000).get() @handle_request(r'^seekid "(?P\d+)" "(?P\d+)"$') @@ -343,9 +343,10 @@ def seekid(context, tlid, seconds): Seeks to the position ``TIME`` (in seconds) of song ``SONGID``. """ - if context.core.playback.current_tlid != tlid: + tl_track = context.core.playback.current_tl_track.get() + if not tl_track or tl_track.tlid != tlid: playid(context, tlid) - context.core.playback.seek(int(seconds) * 1000) + context.core.playback.seek(int(seconds) * 1000).get() @handle_request(r'^setvol (?P[-+]*\d+)$') diff --git a/mopidy/frontends/mpd/protocol/reflection.py b/mopidy/frontends/mpd/protocol/reflection.py index 5af86a1a..d9c35743 100644 --- a/mopidy/frontends/mpd/protocol/reflection.py +++ b/mopidy/frontends/mpd/protocol/reflection.py @@ -1,7 +1,6 @@ from __future__ import unicode_literals from mopidy.frontends.mpd.protocol import handle_request, mpd_commands -from mopidy.frontends.mpd.exceptions import MpdNotImplemented @handle_request(r'^commands$', auth_required=False) @@ -47,8 +46,15 @@ def decoders(context): mime_type: audio/mpeg plugin: mpcdec suffix: mpc + + *Clarifications:* + + - ncmpcpp asks for decoders the first time you open the browse view. By + returning nothing and OK instead of an not implemented error, we avoid + "Not implemented" showing up in the ncmpcpp interface, and we get the + list of playlists without having to enter the browse interface twice. """ - raise MpdNotImplemented # TODO + return # TODO @handle_request(r'^notcommands$', auth_required=False) diff --git a/mopidy/frontends/mpd/protocol/stored_playlists.py b/mopidy/frontends/mpd/protocol/stored_playlists.py index d5d6b2a6..eef1f3d1 100644 --- a/mopidy/frontends/mpd/protocol/stored_playlists.py +++ b/mopidy/frontends/mpd/protocol/stored_playlists.py @@ -7,7 +7,7 @@ from mopidy.frontends.mpd.protocol import handle_request from mopidy.frontends.mpd.translator import playlist_to_mpd_format -@handle_request(r'^listplaylist (?P\S+)$') +@handle_request(r'^listplaylist (?P\w+)$') @handle_request(r'^listplaylist "(?P[^"]+)"$') def listplaylist(context, name): """ @@ -29,7 +29,7 @@ def listplaylist(context, name): return ['file: %s' % t.uri for t in playlists[0].tracks] -@handle_request(r'^listplaylistinfo (?P\S+)$') +@handle_request(r'^listplaylistinfo (?P\w+)$') @handle_request(r'^listplaylistinfo "(?P[^"]+)"$') def listplaylistinfo(context, name): """ @@ -70,9 +70,16 @@ def listplaylists(context): Last-Modified: 2010-02-06T02:10:25Z playlist: b Last-Modified: 2010-02-06T02:11:08Z + + *Clarifications:* + + - ncmpcpp 0.5.10 segfaults if we return 'playlist: ' on a line, so we must + ignore playlists without names, which isn't very useful anyway. """ result = [] for playlist in context.core.playlists.playlists.get(): + if not playlist.name: + continue result.append(('playlist', playlist.name)) last_modified = ( playlist.last_modified or dt.datetime.now()).isoformat() @@ -101,7 +108,7 @@ def load(context, name): playlists = context.core.playlists.filter(name=name).get() if not playlists: raise MpdNoExistError('No such playlist', command='load') - context.core.tracklist.append(playlists[0].tracks) + context.core.tracklist.add(playlists[0].tracks) @handle_request(r'^playlistadd "(?P[^"]+)" "(?P[^"]+)"$') diff --git a/mopidy/frontends/mpris/objects.py b/mopidy/frontends/mpris/objects.py index 51b0d7e8..15ef9383 100644 --- a/mopidy/frontends/mpris/objects.py +++ b/mopidy/frontends/mpris/objects.py @@ -281,7 +281,7 @@ class MprisObject(dbus.service.Object): # is added to the backend. tracks = self.core.library.lookup(uri).get() if tracks: - tl_tracks = self.core.tracklist.append(tracks).get() + tl_tracks = self.core.tracklist.add(tracks).get() self.core.playback.play(tl_tracks[0]) else: logger.debug('Track with URI "%s" not found in library.', uri) @@ -419,8 +419,8 @@ class MprisObject(dbus.service.Object): if not self.get_CanControl(): return False return ( - self.core.playback.current_track.get() is not None or - self.core.playback.track_at_next.get() is not None) + self.core.playback.current_tl_track.get() is not None or + self.core.playback.tl_track_at_next.get() is not None) def get_CanPause(self): if not self.get_CanControl(): @@ -449,7 +449,7 @@ class MprisObject(dbus.service.Object): playlist_uri = self.get_playlist_uri(playlist_id) playlist = self.core.playlists.lookup(playlist_uri).get() if playlist and playlist.tracks: - tl_tracks = self.core.tracklist.append(playlist.tracks).get() + tl_tracks = self.core.tracklist.add(playlist.tracks).get() self.core.playback.play(tl_tracks[0]) @dbus.service.method(dbus_interface=PLAYLISTS_IFACE) diff --git a/mopidy/settings.py b/mopidy/settings.py index 04706bb6..259bc645 100644 --- a/mopidy/settings.py +++ b/mopidy/settings.py @@ -251,3 +251,34 @@ SPOTIFY_PASSWORD = '' #: #: SPOTIFY_BITRATE = 160 SPOTIFY_BITRATE = 160 + +#: Spotify proxy host. +#: +#: Used by :mod:`mopidy.backends.spotify`. +#: +#: Example:: +#: +#: SPOTIFY_PROXY_HOST = u'protocol://host:port' +#: +#: Default:: +#: +#: SPOTIFY_PROXY_HOST = None +SPOTIFY_PROXY_HOST = None + +#: Spotify proxy username. +#: +#: Used by :mod:`mopidy.backends.spotify`. +#: +#: Default:: +#: +#: SPOTIFY_PROXY_USERNAME = None +SPOTIFY_PROXY_USERNAME = None + +#: Spotify proxy password. +#: +#: Used by :mod:`mopidy.backends.spotify` +#: +#: Default:: +#: +#: SPOTIFY_PROXY_PASSWORD = None +SPOTIFY_PROXY_PASSWORD = None diff --git a/requirements/spotify.txt b/requirements/spotify.txt new file mode 100644 index 00000000..c37d4674 --- /dev/null +++ b/requirements/spotify.txt @@ -0,0 +1 @@ +pyspotify >= 1.9, < 1.10 diff --git a/tests/backends/base/__init__.py b/tests/backends/base/__init__.py index ec3ec1df..7dc4bcf6 100644 --- a/tests/backends/base/__init__.py +++ b/tests/backends/base/__init__.py @@ -1,10 +1,9 @@ from __future__ import unicode_literals -def populate_playlist(func): +def populate_tracklist(func): def wrapper(self): - for track in self.tracks: - self.core.tracklist.add(track) + self.tl_tracks = self.core.tracklist.add(self.tracks) return func(self) wrapper.__name__ = func.__name__ diff --git a/tests/backends/base/playback.py b/tests/backends/base/playback.py index 21e377d9..09dffbab 100644 --- a/tests/backends/base/playback.py +++ b/tests/backends/base/playback.py @@ -9,7 +9,7 @@ from mopidy.core import PlaybackState from mopidy.models import Track from tests import unittest -from tests.backends.base import populate_playlist +from tests.backends.base import populate_tracklist # TODO Test 'playlist repeat', e.g. repeat=1,single=0 @@ -40,35 +40,35 @@ class PlaybackControllerTest(object): def test_play_with_empty_playlist_return_value(self): self.assertEqual(self.playback.play(), None) - @populate_playlist + @populate_tracklist def test_play_state(self): self.assertEqual(self.playback.state, PlaybackState.STOPPED) self.playback.play() self.assertEqual(self.playback.state, PlaybackState.PLAYING) - @populate_playlist + @populate_tracklist def test_play_return_value(self): self.assertEqual(self.playback.play(), None) - @populate_playlist + @populate_tracklist def test_play_track_state(self): self.assertEqual(self.playback.state, PlaybackState.STOPPED) self.playback.play(self.tracklist.tl_tracks[-1]) self.assertEqual(self.playback.state, PlaybackState.PLAYING) - @populate_playlist + @populate_tracklist def test_play_track_return_value(self): self.assertEqual(self.playback.play( self.tracklist.tl_tracks[-1]), None) - @populate_playlist + @populate_tracklist def test_play_when_playing(self): self.playback.play() track = self.playback.current_track self.playback.play() self.assertEqual(track, self.playback.current_track) - @populate_playlist + @populate_tracklist def test_play_when_paused(self): self.playback.play() track = self.playback.current_track @@ -77,7 +77,7 @@ class PlaybackControllerTest(object): self.assertEqual(self.playback.state, PlaybackState.PLAYING) self.assertEqual(track, self.playback.current_track) - @populate_playlist + @populate_tracklist def test_play_when_pause_after_next(self): self.playback.play() self.playback.next() @@ -88,17 +88,17 @@ class PlaybackControllerTest(object): self.assertEqual(self.playback.state, PlaybackState.PLAYING) self.assertEqual(track, self.playback.current_track) - @populate_playlist + @populate_tracklist def test_play_sets_current_track(self): self.playback.play() self.assertEqual(self.playback.current_track, self.tracks[0]) - @populate_playlist + @populate_tracklist def test_play_track_sets_current_track(self): self.playback.play(self.tracklist.tl_tracks[-1]) self.assertEqual(self.playback.current_track, self.tracks[-1]) - @populate_playlist + @populate_tracklist def test_play_skips_to_next_track_on_failure(self): # If backend's play() returns False, it is a failure. self.backend.playback.play = lambda track: track != self.tracks[0] @@ -106,7 +106,7 @@ class PlaybackControllerTest(object): self.assertNotEqual(self.playback.current_track, self.tracks[0]) self.assertEqual(self.playback.current_track, self.tracks[1]) - @populate_playlist + @populate_tracklist def test_current_track_after_completed_playlist(self): self.playback.play(self.tracklist.tl_tracks[-1]) self.playback.on_end_of_track() @@ -118,14 +118,14 @@ class PlaybackControllerTest(object): self.assertEqual(self.playback.state, PlaybackState.STOPPED) self.assertEqual(self.playback.current_track, None) - @populate_playlist + @populate_tracklist def test_previous(self): self.playback.play() self.playback.next() self.playback.previous() self.assertEqual(self.playback.current_track, self.tracks[0]) - @populate_playlist + @populate_tracklist def test_previous_more(self): self.playback.play() # At track 0 self.playback.next() # At track 1 @@ -133,13 +133,13 @@ class PlaybackControllerTest(object): self.playback.previous() # At track 1 self.assertEqual(self.playback.current_track, self.tracks[1]) - @populate_playlist + @populate_tracklist def test_previous_return_value(self): self.playback.play() self.playback.next() self.assertEqual(self.playback.previous(), None) - @populate_playlist + @populate_tracklist def test_previous_does_not_trigger_playback(self): self.playback.play() self.playback.next() @@ -147,7 +147,7 @@ class PlaybackControllerTest(object): self.playback.previous() self.assertEqual(self.playback.state, PlaybackState.STOPPED) - @populate_playlist + @populate_tracklist def test_previous_at_start_of_playlist(self): self.playback.previous() self.assertEqual(self.playback.state, PlaybackState.STOPPED) @@ -158,7 +158,7 @@ class PlaybackControllerTest(object): self.assertEqual(self.playback.state, PlaybackState.STOPPED) self.assertEqual(self.playback.current_track, None) - @populate_playlist + @populate_tracklist def test_previous_skips_to_previous_track_on_failure(self): # If backend's play() returns False, it is a failure. self.backend.playback.play = lambda track: track != self.tracks[1] @@ -168,7 +168,7 @@ class PlaybackControllerTest(object): self.assertNotEqual(self.playback.current_track, self.tracks[1]) self.assertEqual(self.playback.current_track, self.tracks[0]) - @populate_playlist + @populate_tracklist def test_next(self): self.playback.play() @@ -181,17 +181,17 @@ class PlaybackControllerTest(object): self.playback.tracklist_position, old_position + 1) self.assertNotEqual(self.playback.current_track.uri, old_uri) - @populate_playlist + @populate_tracklist def test_next_return_value(self): self.playback.play() self.assertEqual(self.playback.next(), None) - @populate_playlist + @populate_tracklist def test_next_does_not_trigger_playback(self): self.playback.next() self.assertEqual(self.playback.state, PlaybackState.STOPPED) - @populate_playlist + @populate_tracklist def test_next_at_end_of_playlist(self): self.playback.play() @@ -204,7 +204,7 @@ class PlaybackControllerTest(object): self.assertEqual(self.playback.state, PlaybackState.STOPPED) - @populate_playlist + @populate_tracklist def test_next_until_end_of_playlist_and_play_from_start(self): self.playback.play() @@ -222,7 +222,7 @@ class PlaybackControllerTest(object): self.playback.next() self.assertEqual(self.playback.state, PlaybackState.STOPPED) - @populate_playlist + @populate_tracklist def test_next_skips_to_next_track_on_failure(self): # If backend's play() returns False, it is a failure. self.backend.playback.play = lambda track: track != self.tracks[1] @@ -232,54 +232,54 @@ class PlaybackControllerTest(object): self.assertNotEqual(self.playback.current_track, self.tracks[1]) self.assertEqual(self.playback.current_track, self.tracks[2]) - @populate_playlist + @populate_tracklist def test_next_track_before_play(self): - self.assertEqual(self.playback.track_at_next, self.tracks[0]) + self.assertEqual(self.playback.tl_track_at_next, self.tl_tracks[0]) - @populate_playlist + @populate_tracklist def test_next_track_during_play(self): self.playback.play() - self.assertEqual(self.playback.track_at_next, self.tracks[1]) + self.assertEqual(self.playback.tl_track_at_next, self.tl_tracks[1]) - @populate_playlist + @populate_tracklist def test_next_track_after_previous(self): self.playback.play() self.playback.next() self.playback.previous() - self.assertEqual(self.playback.track_at_next, self.tracks[1]) + self.assertEqual(self.playback.tl_track_at_next, self.tl_tracks[1]) def test_next_track_empty_playlist(self): - self.assertEqual(self.playback.track_at_next, None) + self.assertEqual(self.playback.tl_track_at_next, None) - @populate_playlist + @populate_tracklist def test_next_track_at_end_of_playlist(self): self.playback.play() for _ in self.tracklist.tl_tracks[1:]: self.playback.next() - self.assertEqual(self.playback.track_at_next, None) + self.assertEqual(self.playback.tl_track_at_next, None) - @populate_playlist + @populate_tracklist def test_next_track_at_end_of_playlist_with_repeat(self): self.playback.repeat = True self.playback.play() for _ in self.tracks[1:]: self.playback.next() - self.assertEqual(self.playback.track_at_next, self.tracks[0]) + self.assertEqual(self.playback.tl_track_at_next, self.tl_tracks[0]) - @populate_playlist + @populate_tracklist def test_next_track_with_random(self): random.seed(1) self.playback.random = True - self.assertEqual(self.playback.track_at_next, self.tracks[2]) + self.assertEqual(self.playback.tl_track_at_next, self.tl_tracks[2]) - @populate_playlist + @populate_tracklist def test_next_with_consume(self): self.playback.consume = True self.playback.play() self.playback.next() self.assertIn(self.tracks[0], self.tracklist.tracks) - @populate_playlist + @populate_tracklist def test_next_with_single_and_repeat(self): self.playback.single = True self.playback.repeat = True @@ -287,7 +287,7 @@ class PlaybackControllerTest(object): self.playback.next() self.assertEqual(self.playback.current_track, self.tracks[1]) - @populate_playlist + @populate_tracklist def test_next_with_random(self): # FIXME feels very fragile random.seed(1) @@ -296,15 +296,15 @@ class PlaybackControllerTest(object): self.playback.next() self.assertEqual(self.playback.current_track, self.tracks[1]) - @populate_playlist + @populate_tracklist def test_next_track_with_random_after_append_playlist(self): random.seed(1) self.playback.random = True - self.assertEqual(self.playback.track_at_next, self.tracks[2]) - self.tracklist.append(self.tracks[:1]) - self.assertEqual(self.playback.track_at_next, self.tracks[1]) + self.assertEqual(self.playback.tl_track_at_next, self.tl_tracks[2]) + self.tracklist.add(self.tracks[:1]) + self.assertEqual(self.playback.tl_track_at_next, self.tl_tracks[1]) - @populate_playlist + @populate_tracklist def test_end_of_track(self): self.playback.play() @@ -317,17 +317,17 @@ class PlaybackControllerTest(object): self.playback.tracklist_position, old_position + 1) self.assertNotEqual(self.playback.current_track.uri, old_uri) - @populate_playlist + @populate_tracklist def test_end_of_track_return_value(self): self.playback.play() self.assertEqual(self.playback.on_end_of_track(), None) - @populate_playlist + @populate_tracklist def test_end_of_track_does_not_trigger_playback(self): self.playback.on_end_of_track() self.assertEqual(self.playback.state, PlaybackState.STOPPED) - @populate_playlist + @populate_tracklist def test_end_of_track_at_end_of_playlist(self): self.playback.play() @@ -340,7 +340,7 @@ class PlaybackControllerTest(object): self.assertEqual(self.playback.state, PlaybackState.STOPPED) - @populate_playlist + @populate_tracklist def test_end_of_track_until_end_of_playlist_and_play_from_start(self): self.playback.play() @@ -358,7 +358,7 @@ class PlaybackControllerTest(object): self.playback.on_end_of_track() self.assertEqual(self.playback.state, PlaybackState.STOPPED) - @populate_playlist + @populate_tracklist def test_end_of_track_skips_to_next_track_on_failure(self): # If backend's play() returns False, it is a failure. self.backend.playback.play = lambda track: track != self.tracks[1] @@ -368,54 +368,54 @@ class PlaybackControllerTest(object): self.assertNotEqual(self.playback.current_track, self.tracks[1]) self.assertEqual(self.playback.current_track, self.tracks[2]) - @populate_playlist + @populate_tracklist def test_end_of_track_track_before_play(self): - self.assertEqual(self.playback.track_at_next, self.tracks[0]) + self.assertEqual(self.playback.tl_track_at_next, self.tl_tracks[0]) - @populate_playlist + @populate_tracklist def test_end_of_track_track_during_play(self): self.playback.play() - self.assertEqual(self.playback.track_at_next, self.tracks[1]) + self.assertEqual(self.playback.tl_track_at_next, self.tl_tracks[1]) - @populate_playlist + @populate_tracklist def test_end_of_track_track_after_previous(self): self.playback.play() self.playback.on_end_of_track() self.playback.previous() - self.assertEqual(self.playback.track_at_next, self.tracks[1]) + self.assertEqual(self.playback.tl_track_at_next, self.tl_tracks[1]) def test_end_of_track_track_empty_playlist(self): - self.assertEqual(self.playback.track_at_next, None) + self.assertEqual(self.playback.tl_track_at_next, None) - @populate_playlist + @populate_tracklist def test_end_of_track_track_at_end_of_playlist(self): self.playback.play() for _ in self.tracklist.tl_tracks[1:]: self.playback.on_end_of_track() - self.assertEqual(self.playback.track_at_next, None) + self.assertEqual(self.playback.tl_track_at_next, None) - @populate_playlist + @populate_tracklist def test_end_of_track_track_at_end_of_playlist_with_repeat(self): self.playback.repeat = True self.playback.play() for _ in self.tracks[1:]: self.playback.on_end_of_track() - self.assertEqual(self.playback.track_at_next, self.tracks[0]) + self.assertEqual(self.playback.tl_track_at_next, self.tl_tracks[0]) - @populate_playlist + @populate_tracklist def test_end_of_track_track_with_random(self): random.seed(1) self.playback.random = True - self.assertEqual(self.playback.track_at_next, self.tracks[2]) + self.assertEqual(self.playback.tl_track_at_next, self.tl_tracks[2]) - @populate_playlist + @populate_tracklist def test_end_of_track_with_consume(self): self.playback.consume = True self.playback.play() self.playback.on_end_of_track() self.assertNotIn(self.tracks[0], self.tracklist.tracks) - @populate_playlist + @populate_tracklist def test_end_of_track_with_random(self): # FIXME feels very fragile random.seed(1) @@ -424,87 +424,89 @@ class PlaybackControllerTest(object): self.playback.on_end_of_track() self.assertEqual(self.playback.current_track, self.tracks[1]) - @populate_playlist + @populate_tracklist def test_end_of_track_track_with_random_after_append_playlist(self): random.seed(1) self.playback.random = True - self.assertEqual(self.playback.track_at_next, self.tracks[2]) - self.tracklist.append(self.tracks[:1]) - self.assertEqual(self.playback.track_at_next, self.tracks[1]) + self.assertEqual(self.playback.tl_track_at_next, self.tl_tracks[2]) + self.tracklist.add(self.tracks[:1]) + self.assertEqual(self.playback.tl_track_at_next, self.tl_tracks[1]) - @populate_playlist + @populate_tracklist def test_previous_track_before_play(self): - self.assertEqual(self.playback.track_at_previous, None) + self.assertEqual(self.playback.tl_track_at_previous, None) - @populate_playlist + @populate_tracklist def test_previous_track_after_play(self): self.playback.play() - self.assertEqual(self.playback.track_at_previous, None) + self.assertEqual(self.playback.tl_track_at_previous, None) - @populate_playlist + @populate_tracklist def test_previous_track_after_next(self): self.playback.play() self.playback.next() - self.assertEqual(self.playback.track_at_previous, self.tracks[0]) + self.assertEqual(self.playback.tl_track_at_previous, self.tl_tracks[0]) - @populate_playlist + @populate_tracklist def test_previous_track_after_previous(self): self.playback.play() # At track 0 self.playback.next() # At track 1 self.playback.next() # At track 2 self.playback.previous() # At track 1 - self.assertEqual(self.playback.track_at_previous, self.tracks[0]) + self.assertEqual(self.playback.tl_track_at_previous, self.tl_tracks[0]) def test_previous_track_empty_playlist(self): - self.assertEqual(self.playback.track_at_previous, None) + self.assertEqual(self.playback.tl_track_at_previous, None) - @populate_playlist + @populate_tracklist def test_previous_track_with_consume(self): self.playback.consume = True for _ in self.tracks: self.playback.next() self.assertEqual( - self.playback.track_at_previous, self.playback.current_track) + self.playback.tl_track_at_previous, + self.playback.current_tl_track) - @populate_playlist + @populate_tracklist def test_previous_track_with_random(self): self.playback.random = True for _ in self.tracks: self.playback.next() self.assertEqual( - self.playback.track_at_previous, self.playback.current_track) + self.playback.tl_track_at_previous, + self.playback.current_tl_track) - @populate_playlist + @populate_tracklist def test_initial_current_track(self): self.assertEqual(self.playback.current_track, None) - @populate_playlist + @populate_tracklist def test_current_track_during_play(self): self.playback.play() self.assertEqual(self.playback.current_track, self.tracks[0]) - @populate_playlist + @populate_tracklist def test_current_track_after_next(self): self.playback.play() self.playback.next() self.assertEqual(self.playback.current_track, self.tracks[1]) - @populate_playlist + @populate_tracklist def test_initial_tracklist_position(self): self.assertEqual(self.playback.tracklist_position, None) - @populate_playlist + @populate_tracklist def test_tracklist_position_during_play(self): self.playback.play() self.assertEqual(self.playback.tracklist_position, 0) - @populate_playlist + @populate_tracklist def test_tracklist_position_after_next(self): self.playback.play() self.playback.next() self.assertEqual(self.playback.tracklist_position, 1) - @populate_playlist + @populate_tracklist def test_tracklist_position_at_end_of_playlist(self): self.playback.play(self.tracklist.tl_tracks[-1]) self.playback.on_end_of_track() @@ -519,12 +521,12 @@ class PlaybackControllerTest(object): wrapper.called = False self.playback.on_tracklist_change = wrapper - self.tracklist.append([Track()]) + self.tracklist.add([Track()]) self.assert_(wrapper.called) @unittest.SkipTest # Blocks for 10ms - @populate_playlist + @populate_tracklist def test_end_of_track_callback_gets_called(self): self.playback.play() result = self.playback.seek(self.tracks[0].length - 10) @@ -532,78 +534,78 @@ class PlaybackControllerTest(object): message = self.core_queue.get(True, 1) self.assertEqual('end_of_track', message['command']) - @populate_playlist + @populate_tracklist def test_on_tracklist_change_when_playing(self): self.playback.play() current_track = self.playback.current_track - self.tracklist.append([self.tracks[2]]) + self.tracklist.add([self.tracks[2]]) self.assertEqual(self.playback.state, PlaybackState.PLAYING) self.assertEqual(self.playback.current_track, current_track) - @populate_playlist + @populate_tracklist def test_on_tracklist_change_when_stopped(self): - self.tracklist.append([self.tracks[2]]) + self.tracklist.add([self.tracks[2]]) self.assertEqual(self.playback.state, PlaybackState.STOPPED) self.assertEqual(self.playback.current_track, None) - @populate_playlist + @populate_tracklist def test_on_tracklist_change_when_paused(self): self.playback.play() self.playback.pause() current_track = self.playback.current_track - self.tracklist.append([self.tracks[2]]) + self.tracklist.add([self.tracks[2]]) self.assertEqual(self.playback.state, PlaybackState.PAUSED) self.assertEqual(self.playback.current_track, current_track) - @populate_playlist + @populate_tracklist def test_pause_when_stopped(self): self.playback.pause() self.assertEqual(self.playback.state, PlaybackState.PAUSED) - @populate_playlist + @populate_tracklist def test_pause_when_playing(self): self.playback.play() self.playback.pause() self.assertEqual(self.playback.state, PlaybackState.PAUSED) - @populate_playlist + @populate_tracklist def test_pause_when_paused(self): self.playback.play() self.playback.pause() self.playback.pause() self.assertEqual(self.playback.state, PlaybackState.PAUSED) - @populate_playlist + @populate_tracklist def test_pause_return_value(self): self.playback.play() self.assertEqual(self.playback.pause(), None) - @populate_playlist + @populate_tracklist def test_resume_when_stopped(self): self.playback.resume() self.assertEqual(self.playback.state, PlaybackState.STOPPED) - @populate_playlist + @populate_tracklist def test_resume_when_playing(self): self.playback.play() self.playback.resume() self.assertEqual(self.playback.state, PlaybackState.PLAYING) - @populate_playlist + @populate_tracklist def test_resume_when_paused(self): self.playback.play() self.playback.pause() self.playback.resume() self.assertEqual(self.playback.state, PlaybackState.PLAYING) - @populate_playlist + @populate_tracklist def test_resume_return_value(self): self.playback.play() self.playback.pause() self.assertEqual(self.playback.resume(), None) @unittest.SkipTest # Uses sleep and might not work with LocalBackend - @populate_playlist + @populate_tracklist def test_resume_continues_from_right_position(self): self.playback.play() time.sleep(0.2) @@ -611,12 +613,12 @@ class PlaybackControllerTest(object): self.playback.resume() self.assertNotEqual(self.playback.time_position, 0) - @populate_playlist + @populate_tracklist def test_seek_when_stopped(self): result = self.playback.seek(1000) self.assert_(result, 'Seek return value was %s' % result) - @populate_playlist + @populate_tracklist def test_seek_when_stopped_updates_position(self): self.playback.seek(1000) position = self.playback.time_position @@ -629,18 +631,18 @@ class PlaybackControllerTest(object): self.playback.seek(0) self.assertEqual(self.playback.state, PlaybackState.STOPPED) - @populate_playlist + @populate_tracklist def test_seek_when_stopped_triggers_play(self): self.playback.seek(0) self.assertEqual(self.playback.state, PlaybackState.PLAYING) - @populate_playlist + @populate_tracklist def test_seek_when_playing(self): self.playback.play() result = self.playback.seek(self.tracks[0].length - 1000) self.assert_(result, 'Seek return value was %s' % result) - @populate_playlist + @populate_tracklist def test_seek_when_playing_updates_position(self): length = self.tracklist.tracks[0].length self.playback.play() @@ -648,14 +650,14 @@ class PlaybackControllerTest(object): position = self.playback.time_position self.assertGreaterEqual(position, length - 1010) - @populate_playlist + @populate_tracklist def test_seek_when_paused(self): self.playback.play() self.playback.pause() result = self.playback.seek(self.tracks[0].length - 1000) self.assert_(result, 'Seek return value was %s' % result) - @populate_playlist + @populate_tracklist def test_seek_when_paused_updates_position(self): length = self.tracklist.tracks[0].length self.playback.play() @@ -664,7 +666,7 @@ class PlaybackControllerTest(object): position = self.playback.time_position self.assertGreaterEqual(position, length - 1010) - @populate_playlist + @populate_tracklist def test_seek_when_paused_triggers_play(self): self.playback.play() self.playback.pause() @@ -672,34 +674,34 @@ class PlaybackControllerTest(object): self.assertEqual(self.playback.state, PlaybackState.PLAYING) @unittest.SkipTest - @populate_playlist + @populate_tracklist def test_seek_beyond_end_of_song(self): # FIXME need to decide return value self.playback.play() result = self.playback.seek(self.tracks[0].length * 100) self.assert_(not result, 'Seek return value was %s' % result) - @populate_playlist + @populate_tracklist def test_seek_beyond_end_of_song_jumps_to_next_song(self): self.playback.play() self.playback.seek(self.tracks[0].length * 100) self.assertEqual(self.playback.current_track, self.tracks[1]) - @populate_playlist + @populate_tracklist def test_seek_beyond_end_of_song_for_last_track(self): self.playback.play(self.tracklist.tl_tracks[-1]) self.playback.seek(self.tracklist.tracks[-1].length * 100) self.assertEqual(self.playback.state, PlaybackState.STOPPED) @unittest.SkipTest - @populate_playlist + @populate_tracklist def test_seek_beyond_start_of_song(self): # FIXME need to decide return value self.playback.play() result = self.playback.seek(-1000) self.assert_(not result, 'Seek return value was %s' % result) - @populate_playlist + @populate_tracklist def test_seek_beyond_start_of_song_update_postion(self): self.playback.play() self.playback.seek(-1000) @@ -707,18 +709,18 @@ class PlaybackControllerTest(object): self.assertGreaterEqual(position, 0) self.assertEqual(self.playback.state, PlaybackState.PLAYING) - @populate_playlist + @populate_tracklist def test_stop_when_stopped(self): self.playback.stop() self.assertEqual(self.playback.state, PlaybackState.STOPPED) - @populate_playlist + @populate_tracklist def test_stop_when_playing(self): self.playback.play() self.playback.stop() self.assertEqual(self.playback.state, PlaybackState.STOPPED) - @populate_playlist + @populate_tracklist def test_stop_when_paused(self): self.playback.play() self.playback.pause() @@ -736,7 +738,7 @@ class PlaybackControllerTest(object): self.assertEqual(self.playback.time_position, 0) - @populate_playlist + @populate_tracklist def test_time_position_when_stopped_with_playlist(self): future = mock.Mock() future.get = mock.Mock(return_value=0) @@ -745,7 +747,7 @@ class PlaybackControllerTest(object): self.assertEqual(self.playback.time_position, 0) @unittest.SkipTest # Uses sleep and does might not work with LocalBackend - @populate_playlist + @populate_tracklist def test_time_position_when_playing(self): self.playback.play() first = self.playback.time_position @@ -754,7 +756,7 @@ class PlaybackControllerTest(object): self.assertGreater(second, first) @unittest.SkipTest # Uses sleep - @populate_playlist + @populate_tracklist def test_time_position_when_paused(self): self.playback.play() time.sleep(0.2) @@ -764,13 +766,13 @@ class PlaybackControllerTest(object): second = self.playback.time_position self.assertEqual(first, second) - @populate_playlist + @populate_tracklist def test_play_with_consume(self): self.playback.consume = True self.playback.play() self.assertEqual(self.playback.current_track, self.tracks[0]) - @populate_playlist + @populate_tracklist def test_playlist_is_empty_after_all_tracks_are_played_with_consume(self): self.playback.consume = True self.playback.play() @@ -778,14 +780,14 @@ class PlaybackControllerTest(object): self.playback.on_end_of_track() self.assertEqual(len(self.tracklist.tracks), 0) - @populate_playlist + @populate_tracklist def test_play_with_random(self): random.seed(1) self.playback.random = True self.playback.play() self.assertEqual(self.playback.current_track, self.tracks[2]) - @populate_playlist + @populate_tracklist def test_previous_with_random(self): random.seed(1) self.playback.random = True @@ -795,13 +797,13 @@ class PlaybackControllerTest(object): self.playback.previous() self.assertEqual(self.playback.current_track, current_track) - @populate_playlist + @populate_tracklist def test_end_of_song_starts_next_track(self): self.playback.play() self.playback.on_end_of_track() self.assertEqual(self.playback.current_track, self.tracks[1]) - @populate_playlist + @populate_tracklist def test_end_of_song_with_single_and_repeat_starts_same(self): self.playback.single = True self.playback.repeat = True @@ -809,7 +811,7 @@ class PlaybackControllerTest(object): self.playback.on_end_of_track() self.assertEqual(self.playback.current_track, self.tracks[0]) - @populate_playlist + @populate_tracklist def test_end_of_playlist_stops(self): self.playback.play(self.tracklist.tl_tracks[-1]) self.playback.on_end_of_track() @@ -824,34 +826,34 @@ class PlaybackControllerTest(object): def test_consume_off_by_default(self): self.assertEqual(self.playback.consume, False) - @populate_playlist + @populate_tracklist def test_random_until_end_of_playlist(self): self.playback.random = True self.playback.play() for _ in self.tracks[1:]: self.playback.next() - self.assertEqual(self.playback.track_at_next, None) + self.assertEqual(self.playback.tl_track_at_next, None) - @populate_playlist + @populate_tracklist def test_random_until_end_of_playlist_and_play_from_start(self): self.playback.repeat = True for _ in self.tracks: self.playback.next() - self.assertNotEqual(self.playback.track_at_next, None) + self.assertNotEqual(self.playback.tl_track_at_next, None) self.assertEqual(self.playback.state, PlaybackState.STOPPED) self.playback.play() self.assertEqual(self.playback.state, PlaybackState.PLAYING) - @populate_playlist + @populate_tracklist def test_random_until_end_of_playlist_with_repeat(self): self.playback.repeat = True self.playback.random = True self.playback.play() for _ in self.tracks: self.playback.next() - self.assertNotEqual(self.playback.track_at_next, None) + self.assertNotEqual(self.playback.tl_track_at_next, None) - @populate_playlist + @populate_tracklist def test_played_track_during_random_not_played_again(self): self.playback.random = True self.playback.play() @@ -861,7 +863,7 @@ class PlaybackControllerTest(object): played.append(self.playback.current_track) self.playback.next() - @populate_playlist + @populate_tracklist def test_playing_track_that_isnt_in_playlist(self): test = lambda: self.playback.play((17, Track())) self.assertRaises(AssertionError, test) diff --git a/tests/backends/base/tracklist.py b/tests/backends/base/tracklist.py index a5fbbcb5..71f44018 100644 --- a/tests/backends/base/tracklist.py +++ b/tests/backends/base/tracklist.py @@ -9,7 +9,7 @@ from mopidy import audio, core from mopidy.core import PlaybackState from mopidy.models import TlTrack, Playlist, Track -from tests.backends.base import populate_playlist +from tests.backends.base import populate_tracklist class TracklistControllerTest(object): @@ -30,54 +30,56 @@ class TracklistControllerTest(object): def test_length(self): self.assertEqual(0, len(self.controller.tl_tracks)) self.assertEqual(0, self.controller.length) - self.controller.append(self.tracks) + self.controller.add(self.tracks) self.assertEqual(3, len(self.controller.tl_tracks)) self.assertEqual(3, self.controller.length) def test_add(self): for track in self.tracks: - tl_track = self.controller.add(track) + tl_tracks = self.controller.add([track]) self.assertEqual(track, self.controller.tracks[-1]) - self.assertEqual(tl_track, self.controller.tl_tracks[-1]) - self.assertEqual(track, tl_track.track) + self.assertEqual(tl_tracks[0], self.controller.tl_tracks[-1]) + self.assertEqual(track, tl_tracks[0].track) def test_add_at_position(self): for track in self.tracks[:-1]: - tl_track = self.controller.add(track, 0) + tl_tracks = self.controller.add([track], 0) self.assertEqual(track, self.controller.tracks[0]) - self.assertEqual(tl_track, self.controller.tl_tracks[0]) - self.assertEqual(track, tl_track.track) + self.assertEqual(tl_tracks[0], self.controller.tl_tracks[0]) + self.assertEqual(track, tl_tracks[0].track) - @populate_playlist + @populate_tracklist def test_add_at_position_outside_of_playlist(self): - test = lambda: self.controller.add( - self.tracks[0], len(self.tracks) + 2) - self.assertRaises(AssertionError, test) + for track in self.tracks: + tl_tracks = self.controller.add([track], len(self.tracks) + 2) + self.assertEqual(track, self.controller.tracks[-1]) + self.assertEqual(tl_tracks[0], self.controller.tl_tracks[-1]) + self.assertEqual(track, tl_tracks[0].track) - @populate_playlist + @populate_tracklist def test_filter_by_tlid(self): tl_track = self.controller.tl_tracks[1] self.assertEqual( [tl_track], self.controller.filter(tlid=tl_track.tlid)) - @populate_playlist + @populate_tracklist def test_filter_by_uri(self): tl_track = self.controller.tl_tracks[1] self.assertEqual( [tl_track], self.controller.filter(uri=tl_track.track.uri)) - @populate_playlist + @populate_tracklist def test_filter_by_uri_returns_nothing_for_invalid_uri(self): self.assertEqual([], self.controller.filter(uri='foobar')) def test_filter_by_uri_returns_single_match(self): track = Track(uri='a') - self.controller.append([Track(uri='z'), track, Track(uri='y')]) + self.controller.add([Track(uri='z'), track, Track(uri='y')]) self.assertEqual(track, self.controller.filter(uri='a')[0].track) def test_filter_by_uri_returns_multiple_matches(self): track = Track(uri='a') - self.controller.append([Track(uri='z'), track, track]) + self.controller.add([Track(uri='z'), track, track]) tl_tracks = self.controller.filter(uri='a') self.assertEqual(track, tl_tracks[0].track) self.assertEqual(track, tl_tracks[1].track) @@ -91,7 +93,7 @@ class TracklistControllerTest(object): track1 = Track(uri='a', name='x') track2 = Track(uri='b', name='x') track3 = Track(uri='b', name='y') - self.controller.append([track1, track2, track3]) + self.controller.add([track1, track2, track3]) self.assertEqual( track1, self.controller.filter(uri='a', name='x')[0].track) self.assertEqual( @@ -103,10 +105,10 @@ class TracklistControllerTest(object): track1 = Track() track2 = Track(uri='b') track3 = Track() - self.controller.append([track1, track2, track3]) + self.controller.add([track1, track2, track3]) self.assertEqual(track2, self.controller.filter(uri='b')[0].track) - @populate_playlist + @populate_tracklist def test_clear(self): self.controller.clear() self.assertEqual(len(self.controller.tracks), 0) @@ -115,49 +117,49 @@ class TracklistControllerTest(object): self.controller.clear() self.assertEqual(len(self.controller.tracks), 0) - @populate_playlist + @populate_tracklist def test_clear_when_playing(self): self.playback.play() self.assertEqual(self.playback.state, PlaybackState.PLAYING) self.controller.clear() self.assertEqual(self.playback.state, PlaybackState.STOPPED) - def test_append_appends_to_the_tracklist(self): - self.controller.append([Track(uri='a'), Track(uri='b')]) + def test_add_appends_to_the_tracklist(self): + self.controller.add([Track(uri='a'), Track(uri='b')]) self.assertEqual(len(self.controller.tracks), 2) - self.controller.append([Track(uri='c'), Track(uri='d')]) + self.controller.add([Track(uri='c'), Track(uri='d')]) self.assertEqual(len(self.controller.tracks), 4) self.assertEqual(self.controller.tracks[0].uri, 'a') self.assertEqual(self.controller.tracks[1].uri, 'b') self.assertEqual(self.controller.tracks[2].uri, 'c') self.assertEqual(self.controller.tracks[3].uri, 'd') - def test_append_does_not_reset_version(self): + def test_add_does_not_reset_version(self): version = self.controller.version - self.controller.append([]) + self.controller.add([]) self.assertEqual(self.controller.version, version) - @populate_playlist - def test_append_preserves_playing_state(self): + @populate_tracklist + def test_add_preserves_playing_state(self): self.playback.play() track = self.playback.current_track - self.controller.append(self.controller.tracks[1:2]) + self.controller.add(self.controller.tracks[1:2]) self.assertEqual(self.playback.state, PlaybackState.PLAYING) self.assertEqual(self.playback.current_track, track) - @populate_playlist - def test_append_preserves_stopped_state(self): - self.controller.append(self.controller.tracks[1:2]) + @populate_tracklist + def test_add_preserves_stopped_state(self): + self.controller.add(self.controller.tracks[1:2]) self.assertEqual(self.playback.state, PlaybackState.STOPPED) self.assertEqual(self.playback.current_track, None) - @populate_playlist - def test_append_returns_the_tl_tracks_that_was_added(self): - tl_tracks = self.controller.append(self.controller.tracks[1:2]) + @populate_tracklist + def test_add_returns_the_tl_tracks_that_was_added(self): + tl_tracks = self.controller.add(self.controller.tracks[1:2]) self.assertEqual(tl_tracks[0].track, self.controller.tracks[1]) def test_index_returns_index_of_track(self): - tl_tracks = self.controller.append(self.tracks) + tl_tracks = self.controller.add(self.tracks) self.assertEquals(0, self.controller.index(tl_tracks[0])) self.assertEquals(1, self.controller.index(tl_tracks[1])) self.assertEquals(2, self.controller.index(tl_tracks[2])) @@ -166,14 +168,14 @@ class TracklistControllerTest(object): test = lambda: self.controller.index(TlTrack(0, Track())) self.assertRaises(ValueError, test) - @populate_playlist + @populate_tracklist def test_move_single(self): self.controller.move(0, 0, 2) tracks = self.controller.tracks self.assertEqual(tracks[2], self.tracks[0]) - @populate_playlist + @populate_tracklist def test_move_group(self): self.controller.move(0, 2, 1) @@ -181,25 +183,25 @@ class TracklistControllerTest(object): self.assertEqual(tracks[1], self.tracks[0]) self.assertEqual(tracks[2], self.tracks[1]) - @populate_playlist + @populate_tracklist def test_moving_track_outside_of_playlist(self): tracks = len(self.controller.tracks) test = lambda: self.controller.move(0, 0, tracks + 5) self.assertRaises(AssertionError, test) - @populate_playlist + @populate_tracklist def test_move_group_outside_of_playlist(self): tracks = len(self.controller.tracks) test = lambda: self.controller.move(0, 2, tracks + 5) self.assertRaises(AssertionError, test) - @populate_playlist + @populate_tracklist def test_move_group_out_of_range(self): tracks = len(self.controller.tracks) test = lambda: self.controller.move(tracks + 2, tracks + 3, 0) self.assertRaises(AssertionError, test) - @populate_playlist + @populate_tracklist def test_move_group_invalid_group(self): test = lambda: self.controller.move(2, 1, 0) self.assertRaises(AssertionError, test) @@ -209,7 +211,7 @@ class TracklistControllerTest(object): tracks2 = self.controller.tracks self.assertNotEqual(id(tracks1), id(tracks2)) - @populate_playlist + @populate_tracklist def test_remove(self): track1 = self.controller.tracks[1] track2 = self.controller.tracks[2] @@ -219,14 +221,14 @@ class TracklistControllerTest(object): self.assertNotIn(track1, self.controller.tracks) self.assertEqual(track2, self.controller.tracks[1]) - @populate_playlist + @populate_tracklist def test_removing_track_that_does_not_exist_does_nothing(self): self.controller.remove(uri='/nonexistant') def test_removing_from_empty_playlist_does_nothing(self): self.controller.remove(uri='/nonexistant') - @populate_playlist + @populate_tracklist def test_shuffle(self): random.seed(1) self.controller.shuffle() @@ -236,7 +238,7 @@ class TracklistControllerTest(object): self.assertNotEqual(self.tracks, shuffled_tracks) self.assertEqual(set(self.tracks), set(shuffled_tracks)) - @populate_playlist + @populate_tracklist def test_shuffle_subset(self): random.seed(1) self.controller.shuffle(1, 3) @@ -247,18 +249,18 @@ class TracklistControllerTest(object): self.assertEqual(self.tracks[0], shuffled_tracks[0]) self.assertEqual(set(self.tracks), set(shuffled_tracks)) - @populate_playlist + @populate_tracklist def test_shuffle_invalid_subset(self): test = lambda: self.controller.shuffle(3, 1) self.assertRaises(AssertionError, test) - @populate_playlist + @populate_tracklist def test_shuffle_superset(self): tracks = len(self.controller.tracks) test = lambda: self.controller.shuffle(1, tracks + 5) self.assertRaises(AssertionError, test) - @populate_playlist + @populate_tracklist def test_shuffle_open_subset(self): random.seed(1) self.controller.shuffle(1) @@ -269,24 +271,24 @@ class TracklistControllerTest(object): self.assertEqual(self.tracks[0], shuffled_tracks[0]) self.assertEqual(set(self.tracks), set(shuffled_tracks)) - @populate_playlist + @populate_tracklist def test_slice_returns_a_subset_of_tracks(self): track_slice = self.controller.slice(1, 3) self.assertEqual(2, len(track_slice)) self.assertEqual(self.tracks[1], track_slice[0].track) self.assertEqual(self.tracks[2], track_slice[1].track) - @populate_playlist + @populate_tracklist def test_slice_returns_empty_list_if_indexes_outside_tracks_list(self): self.assertEqual(0, len(self.controller.slice(7, 8))) self.assertEqual(0, len(self.controller.slice(-1, 1))) - def test_version_does_not_change_when_appending_nothing(self): + def test_version_does_not_change_when_adding_nothing(self): version = self.controller.version - self.controller.append([]) + self.controller.add([]) self.assertEquals(version, self.controller.version) - def test_version_increases_when_appending_something(self): + def test_version_increases_when_adding_something(self): version = self.controller.version - self.controller.append([Track()]) + self.controller.add([Track()]) self.assertLess(version, self.controller.version) diff --git a/tests/backends/local/playback_test.py b/tests/backends/local/playback_test.py index 285270ce..9731f70d 100644 --- a/tests/backends/local/playback_test.py +++ b/tests/backends/local/playback_test.py @@ -27,7 +27,7 @@ class LocalPlaybackControllerTest(PlaybackControllerTest, unittest.TestCase): def add_track(self, path): uri = path_to_uri(path_to_data_dir(path)) track = Track(uri=uri, length=4464) - self.tracklist.add(track) + self.tracklist.add([track]) def test_uri_scheme(self): self.assertIn('file', self.core.uri_schemes) diff --git a/tests/core/events_test.py b/tests/core/events_test.py index 8f969b0d..88f07de6 100644 --- a/tests/core/events_test.py +++ b/tests/core/events_test.py @@ -26,14 +26,14 @@ class BackendEventsTest(unittest.TestCase): self.assertEqual(send.call_args[0][0], 'playlists_loaded') def test_pause_sends_track_playback_paused_event(self, send): - self.core.tracklist.add(Track(uri='dummy:a')) + self.core.tracklist.add([Track(uri='dummy:a')]) self.core.playback.play().get() send.reset_mock() self.core.playback.pause().get() self.assertEqual(send.call_args[0][0], 'track_playback_paused') def test_resume_sends_track_playback_resumed(self, send): - self.core.tracklist.add(Track(uri='dummy:a')) + self.core.tracklist.add([Track(uri='dummy:a')]) self.core.playback.play() self.core.playback.pause().get() send.reset_mock() @@ -41,20 +41,20 @@ class BackendEventsTest(unittest.TestCase): self.assertEqual(send.call_args[0][0], 'track_playback_resumed') def test_play_sends_track_playback_started_event(self, send): - self.core.tracklist.add(Track(uri='dummy:a')) + self.core.tracklist.add([Track(uri='dummy:a')]) send.reset_mock() self.core.playback.play().get() self.assertEqual(send.call_args[0][0], 'track_playback_started') def test_stop_sends_track_playback_ended_event(self, send): - self.core.tracklist.add(Track(uri='dummy:a')) + self.core.tracklist.add([Track(uri='dummy:a')]) self.core.playback.play().get() send.reset_mock() self.core.playback.stop().get() self.assertEqual(send.call_args_list[0][0][0], 'track_playback_ended') def test_seek_sends_seeked_event(self, send): - self.core.tracklist.add(Track(uri='dummy:a', length=40000)) + self.core.tracklist.add([Track(uri='dummy:a', length=40000)]) self.core.playback.play().get() send.reset_mock() self.core.playback.seek(1000).get() @@ -62,35 +62,30 @@ class BackendEventsTest(unittest.TestCase): def test_tracklist_add_sends_tracklist_changed_event(self, send): send.reset_mock() - self.core.tracklist.add(Track(uri='dummy:a')).get() - self.assertEqual(send.call_args[0][0], 'tracklist_changed') - - def test_tracklist_append_sends_tracklist_changed_event(self, send): - send.reset_mock() - self.core.tracklist.append([Track(uri='dummy:a')]).get() + self.core.tracklist.add([Track(uri='dummy:a')]).get() self.assertEqual(send.call_args[0][0], 'tracklist_changed') def test_tracklist_clear_sends_tracklist_changed_event(self, send): - self.core.tracklist.append([Track(uri='dummy:a')]).get() + self.core.tracklist.add([Track(uri='dummy:a')]).get() send.reset_mock() self.core.tracklist.clear().get() self.assertEqual(send.call_args[0][0], 'tracklist_changed') def test_tracklist_move_sends_tracklist_changed_event(self, send): - self.core.tracklist.append( + self.core.tracklist.add( [Track(uri='dummy:a'), Track(uri='dummy:b')]).get() send.reset_mock() self.core.tracklist.move(0, 1, 1).get() self.assertEqual(send.call_args[0][0], 'tracklist_changed') def test_tracklist_remove_sends_tracklist_changed_event(self, send): - self.core.tracklist.append([Track(uri='dummy:a')]).get() + self.core.tracklist.add([Track(uri='dummy:a')]).get() send.reset_mock() self.core.tracklist.remove(uri='dummy:a').get() self.assertEqual(send.call_args[0][0], 'tracklist_changed') def test_tracklist_shuffle_sends_tracklist_changed_event(self, send): - self.core.tracklist.append( + self.core.tracklist.add( [Track(uri='dummy:a'), Track(uri='dummy:b')]).get() send.reset_mock() self.core.tracklist.shuffle().get() diff --git a/tests/core/playback_test.py b/tests/core/playback_test.py index bb3d359f..ffbca506 100644 --- a/tests/core/playback_test.py +++ b/tests/core/playback_test.py @@ -35,7 +35,7 @@ class CorePlaybackTest(unittest.TestCase): self.core = Core(audio=None, backends=[ self.backend1, self.backend2, self.backend3]) - self.core.tracklist.append(self.tracks) + self.core.tracklist.add(self.tracks) self.tl_tracks = self.core.tracklist.tl_tracks self.unplayable_tl_track = self.tl_tracks[2] diff --git a/tests/frontends/mpd/protocol/current_playlist_test.py b/tests/frontends/mpd/protocol/current_playlist_test.py index dd1ba57e..fc4640b1 100644 --- a/tests/frontends/mpd/protocol/current_playlist_test.py +++ b/tests/frontends/mpd/protocol/current_playlist_test.py @@ -10,7 +10,7 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase): needle = Track(uri='dummy://foo') self.backend.library.dummy_library = [ Track(), Track(), needle, Track()] - self.core.tracklist.append( + self.core.tracklist.add( [Track(), Track(), Track(), Track(), Track()]) self.assertEqual(len(self.core.tracklist.tracks.get()), 5) @@ -33,7 +33,7 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase): needle = Track(uri='dummy://foo') self.backend.library.dummy_library = [ Track(), Track(), needle, Track()] - self.core.tracklist.append( + self.core.tracklist.add( [Track(), Track(), Track(), Track(), Track()]) self.assertEqual(len(self.core.tracklist.tracks.get()), 5) @@ -52,7 +52,7 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase): needle = Track(uri='dummy://foo') self.backend.library.dummy_library = [ Track(), Track(), needle, Track()] - self.core.tracklist.append( + self.core.tracklist.add( [Track(), Track(), Track(), Track(), Track()]) self.assertEqual(len(self.core.tracklist.tracks.get()), 5) @@ -67,7 +67,7 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase): needle = Track(uri='dummy://foo') self.backend.library.dummy_library = [ Track(), Track(), needle, Track()] - self.core.tracklist.append( + self.core.tracklist.add( [Track(), Track(), Track(), Track(), Track()]) self.assertEqual(len(self.core.tracklist.tracks.get()), 5) @@ -79,7 +79,7 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase): self.assertEqualResponse('ACK [50@0] {addid} No such song') def test_clear(self): - self.core.tracklist.append( + self.core.tracklist.add( [Track(), Track(), Track(), Track(), Track()]) self.assertEqual(len(self.core.tracklist.tracks.get()), 5) @@ -89,7 +89,7 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase): self.assertInResponse('OK') def test_delete_songpos(self): - self.core.tracklist.append( + self.core.tracklist.add( [Track(), Track(), Track(), Track(), Track()]) self.assertEqual(len(self.core.tracklist.tracks.get()), 5) @@ -99,7 +99,7 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase): self.assertInResponse('OK') def test_delete_songpos_out_of_bounds(self): - self.core.tracklist.append( + self.core.tracklist.add( [Track(), Track(), Track(), Track(), Track()]) self.assertEqual(len(self.core.tracklist.tracks.get()), 5) @@ -108,7 +108,7 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase): self.assertEqualResponse('ACK [2@0] {delete} Bad song index') def test_delete_open_range(self): - self.core.tracklist.append( + self.core.tracklist.add( [Track(), Track(), Track(), Track(), Track()]) self.assertEqual(len(self.core.tracklist.tracks.get()), 5) @@ -117,7 +117,7 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase): self.assertInResponse('OK') def test_delete_closed_range(self): - self.core.tracklist.append( + self.core.tracklist.add( [Track(), Track(), Track(), Track(), Track()]) self.assertEqual(len(self.core.tracklist.tracks.get()), 5) @@ -126,7 +126,7 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase): self.assertInResponse('OK') def test_delete_range_out_of_bounds(self): - self.core.tracklist.append( + self.core.tracklist.add( [Track(), Track(), Track(), Track(), Track()]) self.assertEqual(len(self.core.tracklist.tracks.get()), 5) @@ -135,7 +135,7 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase): self.assertEqualResponse('ACK [2@0] {delete} Bad song index') def test_deleteid(self): - self.core.tracklist.append([Track(), Track()]) + self.core.tracklist.add([Track(), Track()]) self.assertEqual(len(self.core.tracklist.tracks.get()), 2) self.sendRequest('deleteid "1"') @@ -143,7 +143,7 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase): self.assertInResponse('OK') def test_deleteid_does_not_exist(self): - self.core.tracklist.append([Track(), Track()]) + self.core.tracklist.add([Track(), Track()]) self.assertEqual(len(self.core.tracklist.tracks.get()), 2) self.sendRequest('deleteid "12345"') @@ -151,7 +151,7 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase): self.assertEqualResponse('ACK [50@0] {deleteid} No such song') def test_move_songpos(self): - self.core.tracklist.append([ + self.core.tracklist.add([ Track(name='a'), Track(name='b'), Track(name='c'), Track(name='d'), Track(name='e'), Track(name='f'), ]) @@ -167,7 +167,7 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase): self.assertInResponse('OK') def test_move_open_range(self): - self.core.tracklist.append([ + self.core.tracklist.add([ Track(name='a'), Track(name='b'), Track(name='c'), Track(name='d'), Track(name='e'), Track(name='f'), ]) @@ -183,7 +183,7 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase): self.assertInResponse('OK') def test_move_closed_range(self): - self.core.tracklist.append([ + self.core.tracklist.add([ Track(name='a'), Track(name='b'), Track(name='c'), Track(name='d'), Track(name='e'), Track(name='f'), ]) @@ -199,7 +199,7 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase): self.assertInResponse('OK') def test_moveid(self): - self.core.tracklist.append([ + self.core.tracklist.add([ Track(name='a'), Track(name='b'), Track(name='c'), Track(name='d'), Track(name='e'), Track(name='f'), ]) @@ -237,8 +237,7 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase): self.assertEqualResponse('OK') def test_playlistfind_by_filename_in_tracklist(self): - self.core.tracklist.append([ - Track(uri='file:///exists')]) + self.core.tracklist.add([Track(uri='file:///exists')]) self.sendRequest('playlistfind filename "file:///exists"') self.assertInResponse('file: file:///exists') @@ -247,7 +246,7 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase): self.assertInResponse('OK') def test_playlistid_without_songid(self): - self.core.tracklist.append([Track(name='a'), Track(name='b')]) + self.core.tracklist.add([Track(name='a'), Track(name='b')]) self.sendRequest('playlistid') self.assertInResponse('Title: a') @@ -255,7 +254,7 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase): self.assertInResponse('OK') def test_playlistid_with_songid(self): - self.core.tracklist.append([Track(name='a'), Track(name='b')]) + self.core.tracklist.add([Track(name='a'), Track(name='b')]) self.sendRequest('playlistid "1"') self.assertNotInResponse('Title: a') @@ -265,13 +264,13 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase): self.assertInResponse('OK') def test_playlistid_with_not_existing_songid_fails(self): - self.core.tracklist.append([Track(name='a'), Track(name='b')]) + self.core.tracklist.add([Track(name='a'), Track(name='b')]) self.sendRequest('playlistid "25"') self.assertEqualResponse('ACK [50@0] {playlistid} No such song') def test_playlistinfo_without_songpos_or_range(self): - self.core.tracklist.append([ + self.core.tracklist.add([ Track(name='a'), Track(name='b'), Track(name='c'), Track(name='d'), Track(name='e'), Track(name='f'), ]) @@ -294,7 +293,7 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase): def test_playlistinfo_with_songpos(self): # Make the track's CPID not match the playlist position self.core.tracklist.tlid = 17 - self.core.tracklist.append([ + self.core.tracklist.add([ Track(name='a'), Track(name='b'), Track(name='c'), Track(name='d'), Track(name='e'), Track(name='f'), ]) @@ -320,7 +319,7 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase): self.assertEqual(response1, response2) def test_playlistinfo_with_open_range(self): - self.core.tracklist.append([ + self.core.tracklist.add([ Track(name='a'), Track(name='b'), Track(name='c'), Track(name='d'), Track(name='e'), Track(name='f'), ]) @@ -341,7 +340,7 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase): self.assertInResponse('OK') def test_playlistinfo_with_closed_range(self): - self.core.tracklist.append([ + self.core.tracklist.add([ Track(name='a'), Track(name='b'), Track(name='c'), Track(name='d'), Track(name='e'), Track(name='f'), ]) @@ -372,7 +371,7 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase): self.assertEqualResponse('ACK [0@0] {} Not implemented') def test_plchanges_with_lower_version_returns_changes(self): - self.core.tracklist.append( + self.core.tracklist.add( [Track(name='a'), Track(name='b'), Track(name='c')]) self.sendRequest('plchanges "0"') @@ -382,7 +381,7 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase): self.assertInResponse('OK') def test_plchanges_with_equal_version_returns_nothing(self): - self.core.tracklist.append( + self.core.tracklist.add( [Track(name='a'), Track(name='b'), Track(name='c')]) self.assertEqual(self.core.tracklist.version.get(), 1) @@ -393,7 +392,7 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase): self.assertInResponse('OK') def test_plchanges_with_greater_version_returns_nothing(self): - self.core.tracklist.append( + self.core.tracklist.add( [Track(name='a'), Track(name='b'), Track(name='c')]) self.assertEqual(self.core.tracklist.version.get(), 1) @@ -404,7 +403,7 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase): self.assertInResponse('OK') def test_plchanges_with_minus_one_returns_entire_playlist(self): - self.core.tracklist.append( + self.core.tracklist.add( [Track(name='a'), Track(name='b'), Track(name='c')]) self.sendRequest('plchanges "-1"') @@ -414,7 +413,7 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase): self.assertInResponse('OK') def test_plchanges_without_quotes_works(self): - self.core.tracklist.append( + self.core.tracklist.add( [Track(name='a'), Track(name='b'), Track(name='c')]) self.sendRequest('plchanges 0') @@ -424,7 +423,7 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase): self.assertInResponse('OK') def test_plchangesposid(self): - self.core.tracklist.append([Track(), Track(), Track()]) + self.core.tracklist.add([Track(), Track(), Track()]) self.sendRequest('plchangesposid "0"') tl_tracks = self.core.tracklist.tl_tracks.get() @@ -437,7 +436,7 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase): self.assertInResponse('OK') def test_shuffle_without_range(self): - self.core.tracklist.append([ + self.core.tracklist.add([ Track(name='a'), Track(name='b'), Track(name='c'), Track(name='d'), Track(name='e'), Track(name='f'), ]) @@ -448,7 +447,7 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase): self.assertInResponse('OK') def test_shuffle_with_open_range(self): - self.core.tracklist.append([ + self.core.tracklist.add([ Track(name='a'), Track(name='b'), Track(name='c'), Track(name='d'), Track(name='e'), Track(name='f'), ]) @@ -464,7 +463,7 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase): self.assertInResponse('OK') def test_shuffle_with_closed_range(self): - self.core.tracklist.append([ + self.core.tracklist.add([ Track(name='a'), Track(name='b'), Track(name='c'), Track(name='d'), Track(name='e'), Track(name='f'), ]) @@ -480,7 +479,7 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase): self.assertInResponse('OK') def test_swap(self): - self.core.tracklist.append([ + self.core.tracklist.add([ Track(name='a'), Track(name='b'), Track(name='c'), Track(name='d'), Track(name='e'), Track(name='f'), ]) @@ -496,7 +495,7 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase): self.assertInResponse('OK') def test_swapid(self): - self.core.tracklist.append([ + self.core.tracklist.add([ Track(name='a'), Track(name='b'), Track(name='c'), Track(name='d'), Track(name='e'), Track(name='f'), ]) @@ -512,13 +511,13 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase): self.assertInResponse('OK') def test_swapid_with_first_id_unknown_should_ack(self): - self.core.tracklist.append([Track()]) + self.core.tracklist.add([Track()]) self.sendRequest('swapid "0" "4"') self.assertEqualResponse( 'ACK [50@0] {swapid} No such song') def test_swapid_with_second_id_unknown_should_ack(self): - self.core.tracklist.append([Track()]) + self.core.tracklist.add([Track()]) self.sendRequest('swapid "4" "0"') self.assertEqualResponse( 'ACK [50@0] {swapid} No such song') diff --git a/tests/frontends/mpd/protocol/music_db_test.py b/tests/frontends/mpd/protocol/music_db_test.py index 7059c855..4539eb4c 100644 --- a/tests/frontends/mpd/protocol/music_db_test.py +++ b/tests/frontends/mpd/protocol/music_db_test.py @@ -1,5 +1,7 @@ from __future__ import unicode_literals +from mopidy.models import Album, Artist, Track + from tests.frontends.mpd import protocol @@ -119,6 +121,10 @@ class MusicDatabaseFindTest(protocol.BaseTestCase): self.sendRequest('find album "album_what" artist "artist_what"') self.assertInResponse('OK') + def test_find_without_filter_value(self): + self.sendRequest('find "album" ""') + self.assertInResponse('OK') + class MusicDatabaseListTest(protocol.BaseTestCase): def test_list_foo_returns_ack(self): @@ -173,6 +179,18 @@ class MusicDatabaseListTest(protocol.BaseTestCase): 'list "artist" "artist" "anartist" "album" "analbum"') self.assertInResponse('OK') + def test_list_artist_without_filter_value(self): + self.sendRequest('list "artist" "artist" ""') + self.assertInResponse('OK') + + def test_list_artist_should_not_return_artists_without_names(self): + self.backend.library.dummy_find_exact_result = [ + Track(artists=[Artist(name='')])] + + self.sendRequest('list "artist"') + self.assertNotInResponse('Artist: ') + self.assertInResponse('OK') + ### Album def test_list_album_with_quotes(self): @@ -191,6 +209,10 @@ class MusicDatabaseListTest(protocol.BaseTestCase): self.sendRequest('list "album" "anartist"') self.assertInResponse('OK') + def test_list_album_with_artist_name_without_filter_value(self): + self.sendRequest('list "album" ""') + self.assertInResponse('OK') + def test_list_album_by_artist(self): self.sendRequest('list "album" "artist" "anartist"') self.assertInResponse('OK') @@ -216,6 +238,18 @@ class MusicDatabaseListTest(protocol.BaseTestCase): 'list "album" "artist" "anartist" "album" "analbum"') self.assertInResponse('OK') + def test_list_album_without_filter_value(self): + self.sendRequest('list "album" "artist" ""') + self.assertInResponse('OK') + + def test_list_album_should_not_return_albums_without_names(self): + self.backend.library.dummy_find_exact_result = [ + Track(album=Album(name=''))] + + self.sendRequest('list "album"') + self.assertNotInResponse('Album: ') + self.assertInResponse('OK') + ### Date def test_list_date_with_quotes(self): @@ -259,6 +293,17 @@ class MusicDatabaseListTest(protocol.BaseTestCase): self.sendRequest('list "date" "artist" "anartist" "album" "analbum"') self.assertInResponse('OK') + def test_list_date_without_filter_value(self): + self.sendRequest('list "date" "artist" ""') + self.assertInResponse('OK') + + def test_list_date_should_not_return_blank_dates(self): + self.backend.library.dummy_find_exact_result = [Track(date='')] + + self.sendRequest('list "date"') + self.assertNotInResponse('Date: ') + self.assertInResponse('OK') + ### Genre def test_list_genre_with_quotes(self): @@ -303,6 +348,10 @@ class MusicDatabaseListTest(protocol.BaseTestCase): 'list "genre" "artist" "anartist" "album" "analbum"') self.assertInResponse('OK') + def test_list_genre_without_filter_value(self): + self.sendRequest('list "genre" "artist" ""') + self.assertInResponse('OK') + class MusicDatabaseSearchTest(protocol.BaseTestCase): def test_search_album(self): @@ -313,6 +362,10 @@ class MusicDatabaseSearchTest(protocol.BaseTestCase): self.sendRequest('search album "analbum"') self.assertInResponse('OK') + def test_search_album_without_filter_value(self): + self.sendRequest('search "album" ""') + self.assertInResponse('OK') + def test_search_artist(self): self.sendRequest('search "artist" "anartist"') self.assertInResponse('OK') @@ -321,6 +374,10 @@ class MusicDatabaseSearchTest(protocol.BaseTestCase): self.sendRequest('search artist "anartist"') self.assertInResponse('OK') + def test_search_artist_without_filter_value(self): + self.sendRequest('search "artist" ""') + self.assertInResponse('OK') + def test_search_filename(self): self.sendRequest('search "filename" "afilename"') self.assertInResponse('OK') @@ -329,6 +386,10 @@ class MusicDatabaseSearchTest(protocol.BaseTestCase): self.sendRequest('search filename "afilename"') self.assertInResponse('OK') + def test_search_filename_without_filter_value(self): + self.sendRequest('search "filename" ""') + self.assertInResponse('OK') + def test_search_file(self): self.sendRequest('search "file" "afilename"') self.assertInResponse('OK') @@ -337,6 +398,10 @@ class MusicDatabaseSearchTest(protocol.BaseTestCase): self.sendRequest('search file "afilename"') self.assertInResponse('OK') + def test_search_file_without_filter_value(self): + self.sendRequest('search "file" ""') + self.assertInResponse('OK') + def test_search_title(self): self.sendRequest('search "title" "atitle"') self.assertInResponse('OK') @@ -345,6 +410,10 @@ class MusicDatabaseSearchTest(protocol.BaseTestCase): self.sendRequest('search title "atitle"') self.assertInResponse('OK') + def test_search_title_without_filter_value(self): + self.sendRequest('search "title" ""') + self.assertInResponse('OK') + def test_search_any(self): self.sendRequest('search "any" "anything"') self.assertInResponse('OK') @@ -353,6 +422,10 @@ class MusicDatabaseSearchTest(protocol.BaseTestCase): self.sendRequest('search any "anything"') self.assertInResponse('OK') + def test_search_any_without_filter_value(self): + self.sendRequest('search "any" ""') + self.assertInResponse('OK') + def test_search_date(self): self.sendRequest('search "date" "2002-01-01"') self.assertInResponse('OK') @@ -365,6 +438,10 @@ class MusicDatabaseSearchTest(protocol.BaseTestCase): self.sendRequest('search Date "2005"') self.assertInResponse('OK') + def test_search_date_without_filter_value(self): + self.sendRequest('search "date" ""') + self.assertInResponse('OK') + def test_search_else_should_fail(self): self.sendRequest('search "sometype" "something"') self.assertEqualResponse('ACK [2@0] {search} incorrect arguments') diff --git a/tests/frontends/mpd/protocol/playback_test.py b/tests/frontends/mpd/protocol/playback_test.py index f81be241..14168a35 100644 --- a/tests/frontends/mpd/protocol/playback_test.py +++ b/tests/frontends/mpd/protocol/playback_test.py @@ -168,7 +168,7 @@ class PlaybackControlHandlerTest(protocol.BaseTestCase): self.assertInResponse('OK') def test_pause_off(self): - self.core.tracklist.append([Track(uri='dummy:a')]) + self.core.tracklist.add([Track(uri='dummy:a')]) self.sendRequest('play "0"') self.sendRequest('pause "1"') @@ -177,7 +177,7 @@ class PlaybackControlHandlerTest(protocol.BaseTestCase): self.assertInResponse('OK') def test_pause_on(self): - self.core.tracklist.append([Track(uri='dummy:a')]) + self.core.tracklist.add([Track(uri='dummy:a')]) self.sendRequest('play "0"') self.sendRequest('pause "1"') @@ -185,7 +185,7 @@ class PlaybackControlHandlerTest(protocol.BaseTestCase): self.assertInResponse('OK') def test_pause_toggle(self): - self.core.tracklist.append([Track(uri='dummy:a')]) + self.core.tracklist.add([Track(uri='dummy:a')]) self.sendRequest('play "0"') self.assertEqual(PLAYING, self.core.playback.state.get()) @@ -200,28 +200,28 @@ class PlaybackControlHandlerTest(protocol.BaseTestCase): self.assertInResponse('OK') def test_play_without_pos(self): - self.core.tracklist.append([Track(uri='dummy:a')]) + self.core.tracklist.add([Track(uri='dummy:a')]) self.sendRequest('play') self.assertEqual(PLAYING, self.core.playback.state.get()) self.assertInResponse('OK') def test_play_with_pos(self): - self.core.tracklist.append([Track(uri='dummy:a')]) + self.core.tracklist.add([Track(uri='dummy:a')]) self.sendRequest('play "0"') self.assertEqual(PLAYING, self.core.playback.state.get()) self.assertInResponse('OK') def test_play_with_pos_without_quotes(self): - self.core.tracklist.append([Track(uri='dummy:a')]) + self.core.tracklist.add([Track(uri='dummy:a')]) self.sendRequest('play 0') self.assertEqual(PLAYING, self.core.playback.state.get()) self.assertInResponse('OK') def test_play_with_pos_out_of_bounds(self): - self.core.tracklist.append([]) + self.core.tracklist.add([]) self.sendRequest('play "0"') self.assertEqual(STOPPED, self.core.playback.state.get()) @@ -229,10 +229,7 @@ class PlaybackControlHandlerTest(protocol.BaseTestCase): def test_play_minus_one_plays_first_in_playlist_if_no_current_track(self): self.assertEqual(self.core.playback.current_track.get(), None) - self.core.tracklist.append([ - Track(uri='dummy:a'), - Track(uri='dummy:b'), - ]) + self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')]) self.sendRequest('play "-1"') self.assertEqual(PLAYING, self.core.playback.state.get()) @@ -241,10 +238,7 @@ class PlaybackControlHandlerTest(protocol.BaseTestCase): self.assertInResponse('OK') def test_play_minus_one_plays_current_track_if_current_track_is_set(self): - self.core.tracklist.append([ - Track(uri='dummy:a'), - Track(uri='dummy:b'), - ]) + self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')]) self.assertEqual(self.core.playback.current_track.get(), None) self.core.playback.play() self.core.playback.next() @@ -266,8 +260,7 @@ class PlaybackControlHandlerTest(protocol.BaseTestCase): self.assertInResponse('OK') def test_play_minus_is_ignored_if_playing(self): - self.core.tracklist.append([ - Track(uri='dummy:a', length=40000)]) + self.core.tracklist.add([Track(uri='dummy:a', length=40000)]) self.core.playback.seek(30000) self.assertGreaterEqual( self.core.playback.time_position.get(), 30000) @@ -280,8 +273,7 @@ class PlaybackControlHandlerTest(protocol.BaseTestCase): self.assertInResponse('OK') def test_play_minus_one_resumes_if_paused(self): - self.core.tracklist.append([ - Track(uri='dummy:a', length=40000)]) + self.core.tracklist.add([Track(uri='dummy:a', length=40000)]) self.core.playback.seek(30000) self.assertGreaterEqual( self.core.playback.time_position.get(), 30000) @@ -296,14 +288,14 @@ class PlaybackControlHandlerTest(protocol.BaseTestCase): self.assertInResponse('OK') def test_playid(self): - self.core.tracklist.append([Track(uri='dummy:a')]) + self.core.tracklist.add([Track(uri='dummy:a')]) self.sendRequest('playid "0"') self.assertEqual(PLAYING, self.core.playback.state.get()) self.assertInResponse('OK') def test_playid_without_quotes(self): - self.core.tracklist.append([Track(uri='dummy:a')]) + self.core.tracklist.add([Track(uri='dummy:a')]) self.sendRequest('playid 0') self.assertEqual(PLAYING, self.core.playback.state.get()) @@ -311,10 +303,7 @@ class PlaybackControlHandlerTest(protocol.BaseTestCase): def test_playid_minus_1_plays_first_in_playlist_if_no_current_track(self): self.assertEqual(self.core.playback.current_track.get(), None) - self.core.tracklist.append([ - Track(uri='dummy:a'), - Track(uri='dummy:b'), - ]) + self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')]) self.sendRequest('playid "-1"') self.assertEqual(PLAYING, self.core.playback.state.get()) @@ -323,10 +312,7 @@ class PlaybackControlHandlerTest(protocol.BaseTestCase): self.assertInResponse('OK') def test_playid_minus_1_plays_current_track_if_current_track_is_set(self): - self.core.tracklist.append([ - Track(uri='dummy:a'), - Track(uri='dummy:b'), - ]) + self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')]) self.assertEqual(self.core.playback.current_track.get(), None) self.core.playback.play() self.core.playback.next() @@ -348,7 +334,7 @@ class PlaybackControlHandlerTest(protocol.BaseTestCase): self.assertInResponse('OK') def test_playid_minus_is_ignored_if_playing(self): - self.core.tracklist.append([Track(uri='dummy:a', length=40000)]) + self.core.tracklist.add([Track(uri='dummy:a', length=40000)]) self.core.playback.seek(30000) self.assertGreaterEqual( self.core.playback.time_position.get(), 30000) @@ -361,7 +347,7 @@ class PlaybackControlHandlerTest(protocol.BaseTestCase): self.assertInResponse('OK') def test_playid_minus_one_resumes_if_paused(self): - self.core.tracklist.append([Track(uri='dummy:a', length=40000)]) + self.core.tracklist.add([Track(uri='dummy:a', length=40000)]) self.core.playback.seek(30000) self.assertGreaterEqual( self.core.playback.time_position.get(), 30000) @@ -376,7 +362,7 @@ class PlaybackControlHandlerTest(protocol.BaseTestCase): self.assertInResponse('OK') def test_playid_which_does_not_exist(self): - self.core.tracklist.append([Track(uri='dummy:a')]) + self.core.tracklist.add([Track(uri='dummy:a')]) self.sendRequest('playid "12345"') self.assertInResponse('ACK [50@0] {playid} No such song') @@ -386,7 +372,7 @@ class PlaybackControlHandlerTest(protocol.BaseTestCase): self.assertInResponse('OK') def test_seek(self): - self.core.tracklist.append([Track(uri='dummy:a', length=40000)]) + self.core.tracklist.add([Track(uri='dummy:a', length=40000)]) self.sendRequest('seek "0"') self.sendRequest('seek "0" "30"') @@ -395,7 +381,7 @@ class PlaybackControlHandlerTest(protocol.BaseTestCase): def test_seek_with_songpos(self): seek_track = Track(uri='dummy:b', length=40000) - self.core.tracklist.append( + self.core.tracklist.add( [Track(uri='dummy:a', length=40000), seek_track]) self.sendRequest('seek "1" "30"') @@ -403,7 +389,7 @@ class PlaybackControlHandlerTest(protocol.BaseTestCase): self.assertInResponse('OK') def test_seek_without_quotes(self): - self.core.tracklist.append([Track(uri='dummy:a', length=40000)]) + self.core.tracklist.add([Track(uri='dummy:a', length=40000)]) self.sendRequest('seek 0') self.sendRequest('seek 0 30') @@ -412,7 +398,7 @@ class PlaybackControlHandlerTest(protocol.BaseTestCase): self.assertInResponse('OK') def test_seekid(self): - self.core.tracklist.append([Track(uri='dummy:a', length=40000)]) + self.core.tracklist.add([Track(uri='dummy:a', length=40000)]) self.sendRequest('seekid "0" "30"') self.assertGreaterEqual( self.core.playback.time_position.get(), 30000) @@ -420,11 +406,11 @@ class PlaybackControlHandlerTest(protocol.BaseTestCase): def test_seekid_with_tlid(self): seek_track = Track(uri='dummy:b', length=40000) - self.core.tracklist.append( + self.core.tracklist.add( [Track(uri='dummy:a', length=40000), seek_track]) self.sendRequest('seekid "1" "30"') - self.assertEqual(1, self.core.playback.current_tlid.get()) + self.assertEqual(1, self.core.playback.current_tl_track.get().tlid) self.assertEqual(seek_track, self.core.playback.current_track.get()) self.assertInResponse('OK') diff --git a/tests/frontends/mpd/protocol/reflection_test.py b/tests/frontends/mpd/protocol/reflection_test.py index 33032d73..9c07f104 100644 --- a/tests/frontends/mpd/protocol/reflection_test.py +++ b/tests/frontends/mpd/protocol/reflection_test.py @@ -38,7 +38,7 @@ class ReflectionHandlerTest(protocol.BaseTestCase): def test_decoders(self): self.sendRequest('decoders') - self.assertInResponse('ACK [0@0] {} Not implemented') + self.assertInResponse('OK') def test_notcommands_returns_only_kill_and_ok(self): response = self.sendRequest('notcommands') diff --git a/tests/frontends/mpd/protocol/regression_test.py b/tests/frontends/mpd/protocol/regression_test.py index 6b8832e4..0bc488fd 100644 --- a/tests/frontends/mpd/protocol/regression_test.py +++ b/tests/frontends/mpd/protocol/regression_test.py @@ -18,7 +18,7 @@ class IssueGH17RegressionTest(protocol.BaseTestCase): - Press next until you get to the unplayable track """ def test(self): - self.core.tracklist.append([ + self.core.tracklist.add([ Track(uri='dummy:a'), Track(uri='dummy:b'), Track(uri='dummy:error'), @@ -59,7 +59,7 @@ class IssueGH18RegressionTest(protocol.BaseTestCase): """ def test(self): - self.core.tracklist.append([ + self.core.tracklist.add([ Track(uri='dummy:a'), Track(uri='dummy:b'), Track(uri='dummy:c'), Track(uri='dummy:d'), Track(uri='dummy:e'), Track(uri='dummy:f')]) random.seed(1) @@ -95,7 +95,7 @@ class IssueGH22RegressionTest(protocol.BaseTestCase): """ def test(self): - self.core.tracklist.append([ + self.core.tracklist.add([ Track(uri='dummy:a'), Track(uri='dummy:b'), Track(uri='dummy:c'), Track(uri='dummy:d'), Track(uri='dummy:e'), Track(uri='dummy:f')]) random.seed(1) @@ -124,7 +124,7 @@ class IssueGH69RegressionTest(protocol.BaseTestCase): def test(self): self.core.playlists.create('foo') - self.core.tracklist.append([ + self.core.tracklist.add([ Track(uri='dummy:a'), Track(uri='dummy:b'), Track(uri='dummy:c'), Track(uri='dummy:d'), Track(uri='dummy:e'), Track(uri='dummy:f')]) diff --git a/tests/frontends/mpd/protocol/status_test.py b/tests/frontends/mpd/protocol/status_test.py index ef3cf7b2..24f24ab2 100644 --- a/tests/frontends/mpd/protocol/status_test.py +++ b/tests/frontends/mpd/protocol/status_test.py @@ -12,7 +12,7 @@ class StatusHandlerTest(protocol.BaseTestCase): def test_currentsong(self): track = Track() - self.core.tracklist.append([track]) + self.core.tracklist.add([track]) self.core.playback.play() self.sendRequest('currentsong') self.assertInResponse('file: ') diff --git a/tests/frontends/mpd/protocol/stored_playlists_test.py b/tests/frontends/mpd/protocol/stored_playlists_test.py index 6bac95e5..be2afd4c 100644 --- a/tests/frontends/mpd/protocol/stored_playlists_test.py +++ b/tests/frontends/mpd/protocol/stored_playlists_test.py @@ -10,18 +10,18 @@ from tests.frontends.mpd import protocol class PlaylistsHandlerTest(protocol.BaseTestCase): def test_listplaylist(self): self.backend.playlists.playlists = [ - Playlist(name='name', tracks=[Track(uri='file:///dev/urandom')])] + Playlist(name='name', tracks=[Track(uri='dummy:a')])] self.sendRequest('listplaylist "name"') - self.assertInResponse('file: file:///dev/urandom') + self.assertInResponse('file: dummy:a') self.assertInResponse('OK') def test_listplaylist_without_quotes(self): self.backend.playlists.playlists = [ - Playlist(name='name', tracks=[Track(uri='file:///dev/urandom')])] + Playlist(name='name', tracks=[Track(uri='dummy:a')])] self.sendRequest('listplaylist name') - self.assertInResponse('file: file:///dev/urandom') + self.assertInResponse('file: dummy:a') self.assertInResponse('OK') def test_listplaylist_fails_if_no_playlist_is_found(self): @@ -30,20 +30,20 @@ class PlaylistsHandlerTest(protocol.BaseTestCase): def test_listplaylistinfo(self): self.backend.playlists.playlists = [ - Playlist(name='name', tracks=[Track(uri='file:///dev/urandom')])] + Playlist(name='name', tracks=[Track(uri='dummy:a')])] self.sendRequest('listplaylistinfo "name"') - self.assertInResponse('file: file:///dev/urandom') + self.assertInResponse('file: dummy:a') self.assertInResponse('Track: 0') self.assertNotInResponse('Pos: 0') self.assertInResponse('OK') def test_listplaylistinfo_without_quotes(self): self.backend.playlists.playlists = [ - Playlist(name='name', tracks=[Track(uri='file:///dev/urandom')])] + Playlist(name='name', tracks=[Track(uri='dummy:a')])] self.sendRequest('listplaylistinfo name') - self.assertInResponse('file: file:///dev/urandom') + self.assertInResponse('file: dummy:a') self.assertInResponse('Track: 0') self.assertNotInResponse('Pos: 0') self.assertInResponse('OK') @@ -64,8 +64,17 @@ class PlaylistsHandlerTest(protocol.BaseTestCase): self.assertInResponse('Last-Modified: 2001-03-17T13:41:17Z') self.assertInResponse('OK') + def test_listplaylists_ignores_playlists_without_name(self): + last_modified = datetime.datetime(2001, 3, 17, 13, 41, 17, 12345) + self.backend.playlists.playlists = [ + Playlist(name='', last_modified=last_modified)] + + self.sendRequest('listplaylists') + self.assertNotInResponse('playlist: ') + self.assertInResponse('OK') + def test_load_known_playlist_appends_to_tracklist(self): - self.core.tracklist.append([Track(uri='a'), Track(uri='b')]) + self.core.tracklist.add([Track(uri='a'), Track(uri='b')]) self.assertEqual(len(self.core.tracklist.tracks.get()), 2) self.backend.playlists.playlists = [ Playlist(name='A-list', tracks=[ @@ -87,7 +96,7 @@ class PlaylistsHandlerTest(protocol.BaseTestCase): self.assertEqualResponse('ACK [50@0] {load} No such playlist') def test_playlistadd(self): - self.sendRequest('playlistadd "name" "file:///dev/urandom"') + self.sendRequest('playlistadd "name" "dummy:a"') self.assertEqualResponse('ACK [0@0] {} Not implemented') def test_playlistclear(self): diff --git a/tests/frontends/mpd/status_test.py b/tests/frontends/mpd/status_test.py index 6afa5541..d508cbf0 100644 --- a/tests/frontends/mpd/status_test.py +++ b/tests/frontends/mpd/status_test.py @@ -131,21 +131,21 @@ class StatusHandlerTest(unittest.TestCase): self.assertEqual(result['state'], 'pause') def test_status_method_when_playlist_loaded_contains_song(self): - self.core.tracklist.append([Track(uri='dummy:a')]) + self.core.tracklist.add([Track(uri='dummy:a')]) self.core.playback.play() result = dict(status.status(self.context)) self.assertIn('song', result) self.assertGreaterEqual(int(result['song']), 0) def test_status_method_when_playlist_loaded_contains_tlid_as_songid(self): - self.core.tracklist.append([Track(uri='dummy:a')]) + self.core.tracklist.add([Track(uri='dummy:a')]) self.core.playback.play() result = dict(status.status(self.context)) self.assertIn('songid', result) self.assertEqual(int(result['songid']), 0) def test_status_method_when_playing_contains_time_with_no_length(self): - self.core.tracklist.append([Track(uri='dummy:a', length=None)]) + self.core.tracklist.add([Track(uri='dummy:a', length=None)]) self.core.playback.play() result = dict(status.status(self.context)) self.assertIn('time', result) @@ -155,7 +155,7 @@ class StatusHandlerTest(unittest.TestCase): self.assertLessEqual(position, total) def test_status_method_when_playing_contains_time_with_length(self): - self.core.tracklist.append([Track(uri='dummy:a', length=10000)]) + self.core.tracklist.add([Track(uri='dummy:a', length=10000)]) self.core.playback.play() result = dict(status.status(self.context)) self.assertIn('time', result) @@ -165,7 +165,7 @@ class StatusHandlerTest(unittest.TestCase): self.assertLessEqual(position, total) def test_status_method_when_playing_contains_elapsed(self): - self.core.tracklist.append([Track(uri='dummy:a', length=60000)]) + self.core.tracklist.add([Track(uri='dummy:a', length=60000)]) self.core.playback.play() self.core.playback.pause() self.core.playback.seek(59123) @@ -174,7 +174,7 @@ class StatusHandlerTest(unittest.TestCase): self.assertEqual(result['elapsed'], '59.123') def test_status_method_when_starting_playing_contains_elapsed_zero(self): - self.core.tracklist.append([Track(uri='dummy:a', length=10000)]) + self.core.tracklist.add([Track(uri='dummy:a', length=10000)]) self.core.playback.play() self.core.playback.pause() result = dict(status.status(self.context)) @@ -182,7 +182,7 @@ class StatusHandlerTest(unittest.TestCase): self.assertEqual(result['elapsed'], '0.000') def test_status_method_when_playing_contains_bitrate(self): - self.core.tracklist.append([Track(uri='dummy:a', bitrate=320)]) + self.core.tracklist.add([Track(uri='dummy:a', bitrate=320)]) self.core.playback.play() result = dict(status.status(self.context)) self.assertIn('bitrate', result) diff --git a/tests/frontends/mpris/player_interface_test.py b/tests/frontends/mpris/player_interface_test.py index 39b77093..c48ffa98 100644 --- a/tests/frontends/mpris/player_interface_test.py +++ b/tests/frontends/mpris/player_interface_test.py @@ -101,16 +101,14 @@ class PlayerInterfaceTest(unittest.TestCase): def test_set_rate_is_ignored_if_can_control_is_false(self): self.mpris.get_CanControl = lambda *_: False - self.core.tracklist.append([ - Track(uri='dummy:a'), Track(uri='dummy:b')]) + self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')]) self.core.playback.play() self.assertEqual(self.core.playback.state.get(), PLAYING) self.mpris.Set(objects.PLAYER_IFACE, 'Rate', 0) self.assertEqual(self.core.playback.state.get(), PLAYING) def test_set_rate_to_zero_pauses_playback(self): - self.core.tracklist.append([ - Track(uri='dummy:a'), Track(uri='dummy:b')]) + self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')]) self.core.playback.play() self.assertEqual(self.core.playback.state.get(), PLAYING) self.mpris.Set(objects.PLAYER_IFACE, 'Rate', 0) @@ -150,7 +148,7 @@ class PlayerInterfaceTest(unittest.TestCase): self.assertEqual(result['mpris:trackid'], '') def test_get_metadata_has_trackid_based_on_tlid(self): - self.core.tracklist.append([Track(uri='dummy:a')]) + self.core.tracklist.add([Track(uri='dummy:a')]) self.core.playback.play() (tlid, track) = self.core.playback.current_tl_track.get() result = self.mpris.Get(objects.PLAYER_IFACE, 'Metadata') @@ -159,28 +157,28 @@ class PlayerInterfaceTest(unittest.TestCase): result['mpris:trackid'], '/com/mopidy/track/%d' % tlid) def test_get_metadata_has_track_length(self): - self.core.tracklist.append([Track(uri='dummy:a', length=40000)]) + self.core.tracklist.add([Track(uri='dummy:a', length=40000)]) self.core.playback.play() result = self.mpris.Get(objects.PLAYER_IFACE, 'Metadata') self.assertIn('mpris:length', result.keys()) self.assertEqual(result['mpris:length'], 40000000) def test_get_metadata_has_track_uri(self): - self.core.tracklist.append([Track(uri='dummy:a')]) + self.core.tracklist.add([Track(uri='dummy:a')]) self.core.playback.play() result = self.mpris.Get(objects.PLAYER_IFACE, 'Metadata') self.assertIn('xesam:url', result.keys()) self.assertEqual(result['xesam:url'], 'dummy:a') def test_get_metadata_has_track_title(self): - self.core.tracklist.append([Track(name='a')]) + self.core.tracklist.add([Track(name='a')]) self.core.playback.play() result = self.mpris.Get(objects.PLAYER_IFACE, 'Metadata') self.assertIn('xesam:title', result.keys()) self.assertEqual(result['xesam:title'], 'a') def test_get_metadata_has_track_artists(self): - self.core.tracklist.append([Track(artists=[ + self.core.tracklist.add([Track(artists=[ Artist(name='a'), Artist(name='b'), Artist(name=None)])]) self.core.playback.play() result = self.mpris.Get(objects.PLAYER_IFACE, 'Metadata') @@ -188,14 +186,14 @@ class PlayerInterfaceTest(unittest.TestCase): self.assertEqual(result['xesam:artist'], ['a', 'b']) def test_get_metadata_has_track_album(self): - self.core.tracklist.append([Track(album=Album(name='a'))]) + self.core.tracklist.add([Track(album=Album(name='a'))]) self.core.playback.play() result = self.mpris.Get(objects.PLAYER_IFACE, 'Metadata') self.assertIn('xesam:album', result.keys()) self.assertEqual(result['xesam:album'], 'a') def test_get_metadata_has_track_album_artists(self): - self.core.tracklist.append([Track(album=Album(artists=[ + self.core.tracklist.add([Track(album=Album(artists=[ Artist(name='a'), Artist(name='b'), Artist(name=None)]))]) self.core.playback.play() result = self.mpris.Get(objects.PLAYER_IFACE, 'Metadata') @@ -203,7 +201,7 @@ class PlayerInterfaceTest(unittest.TestCase): self.assertEqual(result['xesam:albumArtist'], ['a', 'b']) def test_get_metadata_has_track_number_in_album(self): - self.core.tracklist.append([Track(track_no=7)]) + self.core.tracklist.add([Track(track_no=7)]) self.core.playback.play() result = self.mpris.Get(objects.PLAYER_IFACE, 'Metadata') self.assertIn('xesam:trackNumber', result.keys()) @@ -246,7 +244,7 @@ class PlayerInterfaceTest(unittest.TestCase): self.assertEqual(self.core.playback.volume.get(), 10) def test_get_position_returns_time_position_in_microseconds(self): - self.core.tracklist.append([Track(uri='dummy:a', length=40000)]) + self.core.tracklist.add([Track(uri='dummy:a', length=40000)]) self.core.playback.play() self.core.playback.seek(10000) result_in_microseconds = self.mpris.Get( @@ -270,15 +268,14 @@ class PlayerInterfaceTest(unittest.TestCase): def test_can_go_next_is_true_if_can_control_and_other_next_track(self): self.mpris.get_CanControl = lambda *_: True - self.core.tracklist.append([ - Track(uri='dummy:a'), Track(uri='dummy:b')]) + self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')]) self.core.playback.play() result = self.mpris.Get(objects.PLAYER_IFACE, 'CanGoNext') self.assertTrue(result) def test_can_go_next_is_false_if_next_track_is_the_same(self): self.mpris.get_CanControl = lambda *_: True - self.core.tracklist.append([Track(uri='dummy:a')]) + self.core.tracklist.add([Track(uri='dummy:a')]) self.core.playback.repeat = True self.core.playback.play() result = self.mpris.Get(objects.PLAYER_IFACE, 'CanGoNext') @@ -286,16 +283,14 @@ class PlayerInterfaceTest(unittest.TestCase): def test_can_go_next_is_false_if_can_control_is_false(self): self.mpris.get_CanControl = lambda *_: False - self.core.tracklist.append([ - Track(uri='dummy:a'), Track(uri='dummy:b')]) + self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')]) self.core.playback.play() result = self.mpris.Get(objects.PLAYER_IFACE, 'CanGoNext') self.assertFalse(result) def test_can_go_previous_is_true_if_can_control_and_previous_track(self): self.mpris.get_CanControl = lambda *_: True - self.core.tracklist.append([ - Track(uri='dummy:a'), Track(uri='dummy:b')]) + self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')]) self.core.playback.play() self.core.playback.next() result = self.mpris.Get(objects.PLAYER_IFACE, 'CanGoPrevious') @@ -303,7 +298,7 @@ class PlayerInterfaceTest(unittest.TestCase): def test_can_go_previous_is_false_if_previous_track_is_the_same(self): self.mpris.get_CanControl = lambda *_: True - self.core.tracklist.append([Track(uri='dummy:a')]) + self.core.tracklist.add([Track(uri='dummy:a')]) self.core.playback.repeat = True self.core.playback.play() result = self.mpris.Get(objects.PLAYER_IFACE, 'CanGoPrevious') @@ -311,8 +306,7 @@ class PlayerInterfaceTest(unittest.TestCase): def test_can_go_previous_is_false_if_can_control_is_false(self): self.mpris.get_CanControl = lambda *_: False - self.core.tracklist.append([ - Track(uri='dummy:a'), Track(uri='dummy:b')]) + self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')]) self.core.playback.play() self.core.playback.next() result = self.mpris.Get(objects.PLAYER_IFACE, 'CanGoPrevious') @@ -320,7 +314,7 @@ class PlayerInterfaceTest(unittest.TestCase): def test_can_play_is_true_if_can_control_and_current_track(self): self.mpris.get_CanControl = lambda *_: True - self.core.tracklist.append([Track(uri='dummy:a')]) + self.core.tracklist.add([Track(uri='dummy:a')]) self.core.playback.play() self.assertTrue(self.core.playback.current_track.get()) result = self.mpris.Get(objects.PLAYER_IFACE, 'CanPlay') @@ -363,16 +357,14 @@ class PlayerInterfaceTest(unittest.TestCase): def test_next_is_ignored_if_can_go_next_is_false(self): self.mpris.get_CanGoNext = lambda *_: False - self.core.tracklist.append([ - Track(uri='dummy:a'), Track(uri='dummy:b')]) + self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')]) self.core.playback.play() self.assertEqual(self.core.playback.current_track.get().uri, 'dummy:a') self.mpris.Next() self.assertEqual(self.core.playback.current_track.get().uri, 'dummy:a') def test_next_when_playing_skips_to_next_track_and_keep_playing(self): - self.core.tracklist.append([ - Track(uri='dummy:a'), Track(uri='dummy:b')]) + self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')]) self.core.playback.play() self.assertEqual(self.core.playback.current_track.get().uri, 'dummy:a') self.assertEqual(self.core.playback.state.get(), PLAYING) @@ -381,8 +373,7 @@ class PlayerInterfaceTest(unittest.TestCase): self.assertEqual(self.core.playback.state.get(), PLAYING) def test_next_when_at_end_of_list_should_stop_playback(self): - self.core.tracklist.append([ - Track(uri='dummy:a'), Track(uri='dummy:b')]) + self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')]) self.core.playback.play() self.core.playback.next() self.assertEqual(self.core.playback.current_track.get().uri, 'dummy:b') @@ -391,8 +382,7 @@ class PlayerInterfaceTest(unittest.TestCase): self.assertEqual(self.core.playback.state.get(), STOPPED) def test_next_when_paused_should_skip_to_next_track_and_stay_paused(self): - self.core.tracklist.append([ - Track(uri='dummy:a'), Track(uri='dummy:b')]) + self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')]) self.core.playback.play() self.core.playback.pause() self.assertEqual(self.core.playback.current_track.get().uri, 'dummy:a') @@ -402,8 +392,7 @@ class PlayerInterfaceTest(unittest.TestCase): self.assertEqual(self.core.playback.state.get(), PAUSED) def test_next_when_stopped_skips_to_next_track_and_stay_stopped(self): - self.core.tracklist.append([ - Track(uri='dummy:a'), Track(uri='dummy:b')]) + self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')]) self.core.playback.play() self.core.playback.stop() self.assertEqual(self.core.playback.current_track.get().uri, 'dummy:a') @@ -414,8 +403,7 @@ class PlayerInterfaceTest(unittest.TestCase): def test_previous_is_ignored_if_can_go_previous_is_false(self): self.mpris.get_CanGoPrevious = lambda *_: False - self.core.tracklist.append([ - Track(uri='dummy:a'), Track(uri='dummy:b')]) + self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')]) self.core.playback.play() self.core.playback.next() self.assertEqual(self.core.playback.current_track.get().uri, 'dummy:b') @@ -423,8 +411,7 @@ class PlayerInterfaceTest(unittest.TestCase): self.assertEqual(self.core.playback.current_track.get().uri, 'dummy:b') def test_previous_when_playing_skips_to_prev_track_and_keep_playing(self): - self.core.tracklist.append([ - Track(uri='dummy:a'), Track(uri='dummy:b')]) + self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')]) self.core.playback.play() self.core.playback.next() self.assertEqual(self.core.playback.current_track.get().uri, 'dummy:b') @@ -434,8 +421,7 @@ class PlayerInterfaceTest(unittest.TestCase): self.assertEqual(self.core.playback.state.get(), PLAYING) def test_previous_when_at_start_of_list_should_stop_playback(self): - self.core.tracklist.append([ - Track(uri='dummy:a'), Track(uri='dummy:b')]) + self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')]) self.core.playback.play() self.assertEqual(self.core.playback.current_track.get().uri, 'dummy:a') self.assertEqual(self.core.playback.state.get(), PLAYING) @@ -443,8 +429,7 @@ class PlayerInterfaceTest(unittest.TestCase): self.assertEqual(self.core.playback.state.get(), STOPPED) def test_previous_when_paused_skips_to_previous_track_and_pause(self): - self.core.tracklist.append([ - Track(uri='dummy:a'), Track(uri='dummy:b')]) + self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')]) self.core.playback.play() self.core.playback.next() self.core.playback.pause() @@ -455,8 +440,7 @@ class PlayerInterfaceTest(unittest.TestCase): self.assertEqual(self.core.playback.state.get(), PAUSED) def test_previous_when_stopped_skips_to_previous_track_and_stops(self): - self.core.tracklist.append([ - Track(uri='dummy:a'), Track(uri='dummy:b')]) + self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')]) self.core.playback.play() self.core.playback.next() self.core.playback.stop() @@ -468,24 +452,21 @@ class PlayerInterfaceTest(unittest.TestCase): def test_pause_is_ignored_if_can_pause_is_false(self): self.mpris.get_CanPause = lambda *_: False - self.core.tracklist.append([ - Track(uri='dummy:a'), Track(uri='dummy:b')]) + self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')]) self.core.playback.play() self.assertEqual(self.core.playback.state.get(), PLAYING) self.mpris.Pause() self.assertEqual(self.core.playback.state.get(), PLAYING) def test_pause_when_playing_should_pause_playback(self): - self.core.tracklist.append([ - Track(uri='dummy:a'), Track(uri='dummy:b')]) + self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')]) self.core.playback.play() self.assertEqual(self.core.playback.state.get(), PLAYING) self.mpris.Pause() self.assertEqual(self.core.playback.state.get(), PAUSED) def test_pause_when_paused_has_no_effect(self): - self.core.tracklist.append([ - Track(uri='dummy:a'), Track(uri='dummy:b')]) + self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')]) self.core.playback.play() self.core.playback.pause() self.assertEqual(self.core.playback.state.get(), PAUSED) @@ -494,24 +475,21 @@ class PlayerInterfaceTest(unittest.TestCase): def test_playpause_is_ignored_if_can_pause_is_false(self): self.mpris.get_CanPause = lambda *_: False - self.core.tracklist.append([ - Track(uri='dummy:a'), Track(uri='dummy:b')]) + self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')]) self.core.playback.play() self.assertEqual(self.core.playback.state.get(), PLAYING) self.mpris.PlayPause() self.assertEqual(self.core.playback.state.get(), PLAYING) def test_playpause_when_playing_should_pause_playback(self): - self.core.tracklist.append([ - Track(uri='dummy:a'), Track(uri='dummy:b')]) + self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')]) self.core.playback.play() self.assertEqual(self.core.playback.state.get(), PLAYING) self.mpris.PlayPause() self.assertEqual(self.core.playback.state.get(), PAUSED) def test_playpause_when_paused_should_resume_playback(self): - self.core.tracklist.append([ - Track(uri='dummy:a'), Track(uri='dummy:b')]) + self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')]) self.core.playback.play() self.core.playback.pause() @@ -526,32 +504,28 @@ class PlayerInterfaceTest(unittest.TestCase): self.assertGreaterEqual(after_pause, at_pause) def test_playpause_when_stopped_should_start_playback(self): - self.core.tracklist.append([ - Track(uri='dummy:a'), Track(uri='dummy:b')]) + self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')]) self.assertEqual(self.core.playback.state.get(), STOPPED) self.mpris.PlayPause() self.assertEqual(self.core.playback.state.get(), PLAYING) def test_stop_is_ignored_if_can_control_is_false(self): self.mpris.get_CanControl = lambda *_: False - self.core.tracklist.append([ - Track(uri='dummy:a'), Track(uri='dummy:b')]) + self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')]) self.core.playback.play() self.assertEqual(self.core.playback.state.get(), PLAYING) self.mpris.Stop() self.assertEqual(self.core.playback.state.get(), PLAYING) def test_stop_when_playing_should_stop_playback(self): - self.core.tracklist.append([ - Track(uri='dummy:a'), Track(uri='dummy:b')]) + self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')]) self.core.playback.play() self.assertEqual(self.core.playback.state.get(), PLAYING) self.mpris.Stop() self.assertEqual(self.core.playback.state.get(), STOPPED) def test_stop_when_paused_should_stop_playback(self): - self.core.tracklist.append([ - Track(uri='dummy:a'), Track(uri='dummy:b')]) + self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')]) self.core.playback.play() self.core.playback.pause() self.assertEqual(self.core.playback.state.get(), PAUSED) @@ -560,21 +534,19 @@ class PlayerInterfaceTest(unittest.TestCase): def test_play_is_ignored_if_can_play_is_false(self): self.mpris.get_CanPlay = lambda *_: False - self.core.tracklist.append([ - Track(uri='dummy:a'), Track(uri='dummy:b')]) + self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')]) self.assertEqual(self.core.playback.state.get(), STOPPED) self.mpris.Play() self.assertEqual(self.core.playback.state.get(), STOPPED) def test_play_when_stopped_starts_playback(self): - self.core.tracklist.append([ - Track(uri='dummy:a'), Track(uri='dummy:b')]) + self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')]) self.assertEqual(self.core.playback.state.get(), STOPPED) self.mpris.Play() self.assertEqual(self.core.playback.state.get(), PLAYING) def test_play_after_pause_resumes_from_same_position(self): - self.core.tracklist.append([Track(uri='dummy:a', length=40000)]) + self.core.tracklist.add([Track(uri='dummy:a', length=40000)]) self.core.playback.play() before_pause = self.core.playback.time_position.get() @@ -598,7 +570,7 @@ class PlayerInterfaceTest(unittest.TestCase): def test_seek_is_ignored_if_can_seek_is_false(self): self.mpris.get_CanSeek = lambda *_: False - self.core.tracklist.append([Track(uri='dummy:a', length=40000)]) + self.core.tracklist.add([Track(uri='dummy:a', length=40000)]) self.core.playback.play() before_seek = self.core.playback.time_position.get() @@ -614,7 +586,7 @@ class PlayerInterfaceTest(unittest.TestCase): self.assertLess(after_seek, before_seek + milliseconds_to_seek) def test_seek_seeks_given_microseconds_forward_in_the_current_track(self): - self.core.tracklist.append([Track(uri='dummy:a', length=40000)]) + self.core.tracklist.add([Track(uri='dummy:a', length=40000)]) self.core.playback.play() before_seek = self.core.playback.time_position.get() @@ -631,7 +603,7 @@ class PlayerInterfaceTest(unittest.TestCase): self.assertGreaterEqual(after_seek, before_seek + milliseconds_to_seek) def test_seek_seeks_given_microseconds_backward_if_negative(self): - self.core.tracklist.append([Track(uri='dummy:a', length=40000)]) + self.core.tracklist.add([Track(uri='dummy:a', length=40000)]) self.core.playback.play() self.core.playback.seek(20000) @@ -650,7 +622,7 @@ class PlayerInterfaceTest(unittest.TestCase): self.assertLess(after_seek, before_seek) def test_seek_seeks_to_start_of_track_if_new_position_is_negative(self): - self.core.tracklist.append([Track(uri='dummy:a', length=40000)]) + self.core.tracklist.add([Track(uri='dummy:a', length=40000)]) self.core.playback.play() self.core.playback.seek(20000) @@ -670,7 +642,7 @@ class PlayerInterfaceTest(unittest.TestCase): self.assertGreaterEqual(after_seek, 0) def test_seek_skips_to_next_track_if_new_position_gt_track_length(self): - self.core.tracklist.append([ + self.core.tracklist.add([ Track(uri='dummy:a', length=40000), Track(uri='dummy:b')]) self.core.playback.play() @@ -695,7 +667,7 @@ class PlayerInterfaceTest(unittest.TestCase): def test_set_position_is_ignored_if_can_seek_is_false(self): self.mpris.get_CanSeek = lambda *_: False - self.core.tracklist.append([Track(uri='dummy:a', length=40000)]) + self.core.tracklist.add([Track(uri='dummy:a', length=40000)]) self.core.playback.play() before_set_position = self.core.playback.time_position.get() @@ -713,7 +685,7 @@ class PlayerInterfaceTest(unittest.TestCase): self.assertLess(after_set_position, position_to_set_in_millisec) def test_set_position_sets_the_current_track_position_in_microsecs(self): - self.core.tracklist.append([Track(uri='dummy:a', length=40000)]) + self.core.tracklist.add([Track(uri='dummy:a', length=40000)]) self.core.playback.play() before_set_position = self.core.playback.time_position.get() @@ -734,7 +706,7 @@ class PlayerInterfaceTest(unittest.TestCase): after_set_position, position_to_set_in_millisec) def test_set_position_does_nothing_if_the_position_is_negative(self): - self.core.tracklist.append([Track(uri='dummy:a', length=40000)]) + self.core.tracklist.add([Track(uri='dummy:a', length=40000)]) self.core.playback.play() self.core.playback.seek(20000) @@ -757,7 +729,7 @@ class PlayerInterfaceTest(unittest.TestCase): self.assertEqual(self.core.playback.current_track.get().uri, 'dummy:a') def test_set_position_does_nothing_if_position_is_gt_track_length(self): - self.core.tracklist.append([Track(uri='dummy:a', length=40000)]) + self.core.tracklist.add([Track(uri='dummy:a', length=40000)]) self.core.playback.play() self.core.playback.seek(20000) @@ -780,7 +752,7 @@ class PlayerInterfaceTest(unittest.TestCase): self.assertEqual(self.core.playback.current_track.get().uri, 'dummy:a') def test_set_position_is_noop_if_track_id_isnt_current_track(self): - self.core.tracklist.append([Track(uri='dummy:a', length=40000)]) + self.core.tracklist.add([Track(uri='dummy:a', length=40000)]) self.core.playback.play() self.core.playback.seek(20000) @@ -826,8 +798,7 @@ class PlayerInterfaceTest(unittest.TestCase): def test_open_uri_starts_playback_of_new_track_if_stopped(self): self.mpris.get_CanPlay = lambda *_: True self.backend.library.dummy_library = [Track(uri='dummy:/test/uri')] - self.core.tracklist.append([ - Track(uri='dummy:a'), Track(uri='dummy:b')]) + self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')]) self.assertEqual(self.core.playback.state.get(), STOPPED) self.mpris.OpenUri('dummy:/test/uri') @@ -839,8 +810,7 @@ class PlayerInterfaceTest(unittest.TestCase): def test_open_uri_starts_playback_of_new_track_if_paused(self): self.mpris.get_CanPlay = lambda *_: True self.backend.library.dummy_library = [Track(uri='dummy:/test/uri')] - self.core.tracklist.append([ - Track(uri='dummy:a'), Track(uri='dummy:b')]) + self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')]) self.core.playback.play() self.core.playback.pause() self.assertEqual(self.core.playback.state.get(), PAUSED) @@ -855,8 +825,7 @@ class PlayerInterfaceTest(unittest.TestCase): def test_open_uri_starts_playback_of_new_track_if_playing(self): self.mpris.get_CanPlay = lambda *_: True self.backend.library.dummy_library = [Track(uri='dummy:/test/uri')] - self.core.tracklist.append([ - Track(uri='dummy:a'), Track(uri='dummy:b')]) + self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')]) self.core.playback.play() self.assertEqual(self.core.playback.state.get(), PLAYING) self.assertEqual(self.core.playback.current_track.get().uri, 'dummy:a') diff --git a/tests/frontends/mpris/playlists_interface_test.py b/tests/frontends/mpris/playlists_interface_test.py index 21038d4b..2adffaf3 100644 --- a/tests/frontends/mpris/playlists_interface_test.py +++ b/tests/frontends/mpris/playlists_interface_test.py @@ -44,7 +44,7 @@ class PlayerInterfaceTest(unittest.TestCase): pykka.ActorRegistry.stop_all() def test_activate_playlist_appends_tracks_to_tracklist(self): - self.core.tracklist.append([ + self.core.tracklist.add([ Track(uri='dummy:old-a'), Track(uri='dummy:old-b'), ]) diff --git a/tests/version_test.py b/tests/version_test.py index 978660b0..966b8b94 100644 --- a/tests/version_test.py +++ b/tests/version_test.py @@ -30,5 +30,6 @@ class VersionTest(unittest.TestCase): self.assertLess(SV('0.7.1'), SV('0.7.2')) self.assertLess(SV('0.7.2'), SV('0.7.3')) self.assertLess(SV('0.7.3'), SV('0.8.0')) - self.assertLess(SV('0.8.0'), SV(__version__)) - self.assertLess(SV(__version__), SV('0.8.2')) + self.assertLess(SV('0.8.0'), SV('0.8.1')) + self.assertLess(SV('0.8.1'), SV(__version__)) + self.assertLess(SV(__version__), SV('0.9.1'))