Merge branch 'develop' into feature/http-frontend

This commit is contained in:
Stein Magnus Jodal 2012-11-21 02:02:58 +01:00
commit 532a915db8
38 changed files with 954 additions and 796 deletions

View File

@ -5,14 +5,30 @@ Changes
This change log is used to track all major changes to Mopidy. This change log is used to track all major changes to Mopidy.
v0.9.0 (in development) v0.9.0 (2012-11-21)
======================= ===================
**Multiple backends support**
Support for using the local and Spotify backends simultaneously have for a very Support for using the local and Spotify backends simultaneously have for a very
long time been our most requested feature. Finally, it's here! 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. - 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` 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, 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 As always, see :mod:`mopidy.settings` for the full list of available
settings. 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 <mopidy.audio.mixers.nad>` 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 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 - 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 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 Frontends no longer know anything about the backends. They just use the
:ref:`core-api`. :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 - The dependency graph between the core controllers and the backend providers
have been straightened out, so that we don't have any circular dependencies. 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 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. broadcasting of events to listeners, through e.g.
:class:`mopidy.core.CoreListener` and :class:`mopidy.audio.AudioListener`. :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 - All dependencies are now explicitly passed to the constructors of the
frontends, core, and the backends. This makes testing each layer with frontends, core, and the backends. This makes testing each layer with
dummy/mocked lower layers easier than with the old variant, where dummy/mocked lower layers easier than with the old variant, where
dependencies where looked up in Pykka's actor registry. dependencies where looked up in Pykka's actor registry.
- Renamed "current playlist" to "tracklist" everywhere, including the core API - All properties in the core API now got getters, and setters if setting them
used by frontends. 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 *Models:*
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 <mopidy.audio.mixers.nad>` 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.
- Added :attr:`mopidy.models.Album.date` attribute. It has the same format as - Added :attr:`mopidy.models.Album.date` attribute. It has the same format as
the existing :attr:`mopidy.models.Track.date`. 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 - :meth:`mopidy.core.LibraryController.find_exact` and
enabled via the ``--debug-thread`` option or :meth:`mopidy.core.LibraryController.search` now returns plain lists of
:attr:`mopidy.settings.DEBUG_THREAD` setting a ``SIGUSR1`` signal will dump tracks instead of playlist objects.
the traceback for all running threads.
- Make the entire code base use unicode strings by default, and only fall back - :meth:`mopidy.core.LibraryController.lookup` now returns a list of tracks
to bytestrings where it is required. Another step closer to Python 3. 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_`` *Playback:*
to exist in the settings file.
- The MPD commands ``search`` and ``find`` now allows the key ``file``, which - The base playback provider has been updated with sane default behavior
is used by ncmpcpp instead of ``filename``. 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 - Removed :attr:`mopidy.core.PlaybackController.track_at_previous`. Use
URI. (Fixes: :issue:`233`) :attr:`mopidy.core.PlaybackController.tl_track_at_previous` instead.
- :meth:`mopidy.core.TracklistController.append` now returns a list of the - Removed :attr:`mopidy.core.PlaybackController.track_at_next`. Use
:class:`mopidy.models.TlTrack` instances that was added to the tracklist. :attr:`mopidy.core.PlaybackController.tl_track_at_next` instead.
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_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 - When the tracklist is changed, we now trigger the new
:meth:`mopidy.core.CoreListener.tracklist_changed` event. Previously we :meth:`mopidy.core.CoreListener.tracklist_changed` event. Previously we
triggered :meth:`mopidy.core.CoreListener.playlist_changed`, which is triggered :meth:`mopidy.core.CoreListener.playlist_changed`, which is
intended for stored playlists, not the tracklist. intended for stored playlists, not the tracklist.
- The event :meth:`mopidy.core.CoreListener.playlist_changed` has been changed *Towards Python 3 support:*
to include the playlist that was changed.
- The MPRIS playlists interface is now supported by our MPRIS frontend. This - Make the entire code base use unicode strings by default, and only fall back
means that you now can select playlists to queue and play from the Ubuntu to bytestrings where it is required. Another step closer to Python 3.
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.
v0.8.1 (2012-10-30) v0.8.1 (2012-10-30)

View File

@ -23,7 +23,7 @@ if (isinstance(pykka.__version__, basestring)
warnings.filterwarnings('ignore', 'could not open display') warnings.filterwarnings('ignore', 'could not open display')
__version__ = '0.8.1' __version__ = '0.9.0'
from mopidy import settings as default_settings_module from mopidy import settings as default_settings_module

View File

@ -37,9 +37,11 @@ class DummyLibraryProvider(base.BaseLibraryProvider):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(DummyLibraryProvider, self).__init__(*args, **kwargs) super(DummyLibraryProvider, self).__init__(*args, **kwargs)
self.dummy_library = [] self.dummy_library = []
self.dummy_find_exact_result = []
self.dummy_search_result = []
def find_exact(self, **query): def find_exact(self, **query):
return [] return self.dummy_find_exact_result
def lookup(self, uri): def lookup(self, uri):
return filter(lambda t: uri == t.uri, self.dummy_library) return filter(lambda t: uri == t.uri, self.dummy_library)
@ -48,7 +50,7 @@ class DummyLibraryProvider(base.BaseLibraryProvider):
pass pass
def search(self, **query): def search(self, **query):
return [] return self.dummy_search_result
class DummyPlaybackProvider(base.BasePlaybackProvider): class DummyPlaybackProvider(base.BasePlaybackProvider):

View File

@ -21,7 +21,7 @@ https://github.com/mopidy/mopidy/issues?labels=Spotify+backend
**Dependencies:** **Dependencies:**
- libspotify >= 12, < 13 (libspotify12 package from apt.mopidy.com) - 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:** **Settings:**

View File

@ -31,9 +31,14 @@ class SpotifyBackend(pykka.ThreadingActor, base.Backend):
# Fail early if settings are not present # Fail early if settings are not present
username = settings.SPOTIFY_USERNAME username = settings.SPOTIFY_USERNAME
password = settings.SPOTIFY_PASSWORD password = settings.SPOTIFY_PASSWORD
proxy = settings.SPOTIFY_PROXY_HOST
proxy_username = settings.SPOTIFY_PROXY_USERNAME
proxy_password = settings.SPOTIFY_PROXY_PASSWORD
self.spotify = SpotifySessionManager( 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): def on_start(self):
logger.info('Mopidy uses SPOTIFY(R) CORE') logger.info('Mopidy uses SPOTIFY(R) CORE')

View File

@ -46,10 +46,11 @@ class SpotifyPlaybackProvider(base.BasePlaybackProvider):
def resume(self): def resume(self):
time_position = self.get_time_position() time_position = self.get_time_position()
self._timer.resume() self._timer.resume()
self.audio.prepare_change()
return self.seek(time_position) result = self.seek(time_position)
self.audio.start_playback()
return result
def seek(self, time_position): def seek(self, time_position):
self.backend.spotify.session.seek(time_position) self.backend.spotify.session.seek(time_position)

View File

@ -10,7 +10,7 @@ import threading
from spotify.manager import SpotifySessionManager as PyspotifySessionManager from spotify.manager import SpotifySessionManager as PyspotifySessionManager
from mopidy import settings from mopidy import audio, settings
from mopidy.backends.listener import BackendListener from mopidy.backends.listener import BackendListener
from mopidy.utils import process, versioning 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') appkey_file = os.path.join(os.path.dirname(__file__), 'spotify_appkey.key')
user_agent = 'Mopidy %s' % versioning.get_version() user_agent = 'Mopidy %s' % versioning.get_version()
def __init__(self, username, password, audio, backend_ref): def __init__(self, username, password, audio, backend_ref, proxy=None,
PyspotifySessionManager.__init__(self, username, password) 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) process.BaseThread.__init__(self)
self.name = 'SpotifyThread' self.name = 'SpotifyThread'
@ -88,7 +92,8 @@ class SpotifySessionManager(process.BaseThread, PyspotifySessionManager):
logger.info('Spotify connection OK') logger.info('Spotify connection OK')
else: else:
logger.error('Spotify connection error: %s', error) 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): def message_to_user(self, session, message):
"""Callback used by pyspotify""" """Callback used by pyspotify"""

View File

@ -56,6 +56,10 @@ def to_mopidy_playlist(spotify_playlist):
uri = str(Link.from_playlist(spotify_playlist)) uri = str(Link.from_playlist(spotify_playlist))
if not spotify_playlist.is_loaded(): if not spotify_playlist.is_loaded():
return Playlist(uri=uri, name='[loading...]') 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( return Playlist(
uri=uri, uri=uri,
name=spotify_playlist.name(), name=spotify_playlist.name(),

View File

@ -46,14 +46,15 @@ class Core(pykka.ThreadingActor, AudioListener, BackendListener):
self.tracklist = TracklistController(core=self) self.tracklist = TracklistController(core=self)
@property def get_uri_schemes(self):
def uri_schemes(self):
"""List of URI schemes we can handle"""
futures = [b.uri_schemes for b in self.backends] futures = [b.uri_schemes for b in self.backends]
results = pykka.get_all(futures) results = pykka.get_all(futures)
uri_schemes = itertools.chain(*results) uri_schemes = itertools.chain(*results)
return sorted(uri_schemes) return sorted(uri_schemes)
uri_schemes = property(get_uri_schemes)
"""List of URI schemes we can handle"""
def reached_end_of_stream(self): def reached_end_of_stream(self):
self.playback.on_end_of_track() self.playback.on_end_of_track()

View File

@ -12,56 +12,12 @@ from . import listener
logger = logging.getLogger('mopidy.core') 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): class PlaybackController(object):
# pylint: disable = R0902 # pylint: disable = R0902
# Too many instance attributes # Too many instance attributes
pykka_traversable = True 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): def __init__(self, audio, backends, core):
self.audio = audio self.audio = audio
self.backends = backends self.backends = backends
@ -79,42 +35,128 @@ class PlaybackController(object):
uri_scheme = urlparse.urlparse(uri).scheme uri_scheme = urlparse.urlparse(uri).scheme
return self.backends.with_playback_by_uri_scheme.get(uri_scheme, None) return self.backends.with_playback_by_uri_scheme.get(uri_scheme, None)
def _get_tlid(self, tl_track): ### Properties
if tl_track is None:
return None
return tl_track.tlid
def _get_track(self, tl_track): def get_consume(self):
if tl_track is None: return getattr(self, '_consume', False)
return None
return tl_track.track
@property def set_consume(self, value):
def current_tlid(self): if self.get_consume() != value:
""" self._trigger_options_changed()
The TLID (tracklist ID) of the currently playing or selected return setattr(self, '_consume', value)
track.
Read-only. Extracted from :attr:`current_tl_track` for convenience. consume = property(get_consume, set_consume)
""" """
return self._get_tlid(self.current_tl_track) :class:`True`
Tracks are removed from the playlist when they have been played.
:class:`False`
Tracks are not removed from the playlist.
"""
@property current_tl_track = None
def current_track(self): """
""" The currently playing or selected :class:`mopidy.models.TlTrack`, or
The currently playing or selected :class:`mopidy.models.Track`. :class:`None`.
"""
Read-only. Extracted from :attr:`current_tl_track` for convenience. def get_current_track(self):
""" return self.current_tl_track and self.current_tl_track.track
return self._get_track(self.current_tl_track)
@property current_track = property(get_current_track)
def tracklist_position(self): """
""" The currently playing or selected :class:`mopidy.models.Track`.
The position of the current track in the tracklist.
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: if self.current_tl_track is None:
return None return None
try: try:
@ -122,25 +164,14 @@ class PlaybackController(object):
except ValueError: except ValueError:
return None return None
@property tracklist_position = property(get_tracklist_position)
def track_at_eot(self): """
""" The position of the current track in the tracklist.
The track that will be played at the end of the current track.
Read-only. A :class:`mopidy.models.Track` extracted from Read-only.
:attr:`tl_track_at_eot` for convenience. """
"""
return self._get_track(self.tl_track_at_eot)
@property def get_tl_track_at_eot(self):
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`.
"""
# pylint: disable = R0911 # pylint: disable = R0911
# Too many return statements # Too many return statements
@ -173,28 +204,16 @@ class PlaybackController(object):
except IndexError: except IndexError:
return None return None
@property tl_track_at_eot = property(get_tl_track_at_eot)
def track_at_next(self): """
""" The track that will be played at the end of the current track.
The track that will be played if calling :meth:`next()`.
Read-only. A :class:`mopidy.models.Track` extracted from Read-only. A :class:`mopidy.models.TlTrack`.
:attr:`tl_track_at_next` for convenience.
"""
return self._get_track(self.tl_track_at_next)
@property Not necessarily the same track as :attr:`tl_track_at_next`.
def tl_track_at_next(self): """
"""
The track that will be played if calling :meth:`next()`.
Read-only. A :class:`mopidy.models.TlTrack`. def get_tl_track_at_next(self):
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.
"""
tl_tracks = self.core.tracklist.tl_tracks tl_tracks = self.core.tracklist.tl_tracks
if not tl_tracks: if not tl_tracks:
@ -221,27 +240,19 @@ class PlaybackController(object):
except IndexError: except IndexError:
return None return None
@property tl_track_at_next = property(get_tl_track_at_next)
def track_at_previous(self): """
""" The track that will be played if calling :meth:`next()`.
The track that will be played if calling :meth:`previous()`.
Read-only. A :class:`mopidy.models.Track` extracted from Read-only. A :class:`mopidy.models.TlTrack`.
:attr:`tl_track_at_previous` for convenience.
"""
return self._get_track(self.tl_track_at_previous)
@property For normal playback this is the next track in the playlist. If repeat
def tl_track_at_previous(self): 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
The track that will be played if calling :meth:`previous()`. before the list repeats.
"""
A :class:`mopidy.models.TlTrack`. def get_tl_track_at_previous(self):
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.
"""
if self.repeat or self.consume or self.random: if self.repeat or self.consume or self.random:
return self.current_tl_track return self.current_tl_track
@ -250,59 +261,36 @@ class PlaybackController(object):
return self.core.tracklist.tl_tracks[self.tracklist_position - 1] return self.core.tracklist.tl_tracks[self.tracklist_position - 1]
@property tl_track_at_previous = property(get_tl_track_at_previous)
def state(self): """
""" The track that will be played if calling :meth:`previous()`.
The playback state. Must be :attr:`PLAYING`, :attr:`PAUSED`, or
:attr:`STOPPED`.
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" ] def get_volume(self):
"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`"""
if self.audio: if self.audio:
return self.audio.get_volume().get() return self.audio.get_volume().get()
else: else:
# For testing # For testing
return self._volume return self._volume
@volume.setter # noqa def set_volume(self, volume):
def volume(self, volume):
if self.audio: if self.audio:
self.audio.set_volume(volume) self.audio.set_volume(volume)
else: else:
# For testing # For testing
self._volume = volume 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): def change_track(self, tl_track, on_error_step=1):
""" """
Change to the given track, keeping the current playback state. Change to the given track, keeping the current playback state.
@ -324,6 +312,8 @@ class PlaybackController(object):
def on_end_of_track(self): def on_end_of_track(self):
""" """
Tell the playback controller that end of track is reached. Tell the playback controller that end of track is reached.
Used by event handler in :class:`mopidy.core.Core`.
""" """
if self.state == PlaybackState.STOPPED: if self.state == PlaybackState.STOPPED:
return return
@ -343,7 +333,7 @@ class PlaybackController(object):
""" """
Tell the playback controller that the current playlist has changed. 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._first_shuffle = True
self._shuffled = [] self._shuffled = []

View File

@ -15,18 +15,19 @@ class PlaylistsController(object):
self.backends = backends self.backends = backends
self.core = core self.core = core
@property def get_playlists(self):
def playlists(self):
"""
The available playlists.
Read-only. List of :class:`mopidy.models.Playlist`.
"""
futures = [ futures = [
b.playlists.playlists for b in self.backends.with_playlists] b.playlists.playlists for b in self.backends.with_playlists]
results = pykka.get_all(futures) results = pykka.get_all(futures)
return list(itertools.chain(*results)) 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): def create(self, name, uri_scheme=None):
""" """
Create a new playlist. Create a new playlist.

View File

@ -20,89 +20,77 @@ class TracklistController(object):
self._tl_tracks = [] self._tl_tracks = []
self._version = 0 self._version = 0
@property def get_tl_tracks(self):
def tl_tracks(self):
"""
List of :class:`mopidy.models.TlTrack`.
Read-only.
"""
return self._tl_tracks[:] return self._tl_tracks[:]
@property tl_tracks = property(get_tl_tracks)
def tracks(self): """
""" List of :class:`mopidy.models.TlTrack`.
List of :class:`mopidy.models.Track` in the tracklist.
Read-only. Read-only.
""" """
def get_tracks(self):
return [tl_track.track for tl_track in self._tl_tracks] return [tl_track.track for tl_track in self._tl_tracks]
@property tracks = property(get_tracks)
def length(self): """
""" List of :class:`mopidy.models.Track` in the tracklist.
Length of the tracklist.
""" Read-only.
"""
def get_length(self):
return len(self._tl_tracks) return len(self._tl_tracks)
@property length = property(get_length)
def version(self): """Length of the tracklist."""
"""
The tracklist version. Integer which is increased every time the def get_version(self):
tracklist is changed. Is not reset before Mopidy is restarted.
"""
return self._version return self._version
@version.setter # noqa def _increase_version(self):
def version(self, version): self._version += 1
self._version = version
self._core.playback.on_tracklist_change() self._core.playback.on_tracklist_change()
self._trigger_tracklist_changed() 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` If ``at_position`` is given, the tracks placed at the given position in
event. the tracklist. If ``at_position`` is not given, the tracks are appended
to the end of the tracklist.
:param track: track to add Triggers the :meth:`mopidy.core.CoreListener.tracklist_changed` event.
:type track: :class:`mopidy.models.Track`
:param tracks: tracks to add
:type tracks: list of :class:`mopidy.models.Track`
:param at_position: position in tracklist to add track :param at_position: position in tracklist to add track
:type at_position: int or :class:`None` :type at_position: int or :class:`None`
:param increase_version: if the tracklist version should be increased :rtype: list of :class:`mopidy.models.TlTrack`
: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`
""" """
tl_tracks = [] tl_tracks = []
for track in 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: if tl_tracks:
self.version += 1 self._increase_version()
return tl_tracks return tl_tracks
@ -110,11 +98,10 @@ class TracklistController(object):
""" """
Clear the tracklist. Clear the tracklist.
Triggers the :method:`mopidy.core.CoreListener.tracklist_changed` Triggers the :meth:`mopidy.core.CoreListener.tracklist_changed` event.
event.
""" """
self._tl_tracks = [] self._tl_tracks = []
self.version += 1 self._increase_version()
def filter(self, **criteria): def filter(self, **criteria):
""" """
@ -156,8 +143,7 @@ class TracklistController(object):
""" """
Move the tracks in the slice ``[start:end]`` to ``to_position``. Move the tracks in the slice ``[start:end]`` to ``to_position``.
Triggers the :method:`mopidy.core.CoreListener.tracklist_changed` Triggers the :meth:`mopidy.core.CoreListener.tracklist_changed` event.
event.
:param start: position of first track to move :param start: position of first track to move
:type start: int :type start: int
@ -184,7 +170,7 @@ class TracklistController(object):
new_tl_tracks.insert(to_position, tl_track) new_tl_tracks.insert(to_position, tl_track)
to_position += 1 to_position += 1
self._tl_tracks = new_tl_tracks self._tl_tracks = new_tl_tracks
self.version += 1 self._increase_version()
def remove(self, **criteria): def remove(self, **criteria):
""" """
@ -192,8 +178,7 @@ class TracklistController(object):
Uses :meth:`filter()` to lookup the tracks to remove. Uses :meth:`filter()` to lookup the tracks to remove.
Triggers the :method:`mopidy.core.CoreListener.tracklist_changed` Triggers the :meth:`mopidy.core.CoreListener.tracklist_changed` event.
event.
:param criteria: on or more criteria to match by :param criteria: on or more criteria to match by
:type criteria: dict :type criteria: dict
@ -203,7 +188,7 @@ class TracklistController(object):
for tl_track in tl_tracks: for tl_track in tl_tracks:
position = self._tl_tracks.index(tl_track) position = self._tl_tracks.index(tl_track)
del self._tl_tracks[position] del self._tl_tracks[position]
self.version += 1 self._increase_version()
return tl_tracks return tl_tracks
def shuffle(self, start=None, end=None): 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 entire tracklist. If ``start`` and ``end`` is given only
shuffles the slice ``[start:end]``. shuffles the slice ``[start:end]``.
Triggers the :method:`mopidy.core.CoreListener.tracklist_changed` Triggers the :meth:`mopidy.core.CoreListener.tracklist_changed` event.
event.
:param start: position of first track to shuffle :param start: position of first track to shuffle
:type start: int or :class:`None` :type start: int or :class:`None`
@ -236,7 +220,7 @@ class TracklistController(object):
after = tl_tracks[end or len(tl_tracks):] after = tl_tracks[end or len(tl_tracks):]
random.shuffle(shuffled) random.shuffle(shuffled)
self._tl_tracks = before + shuffled + after self._tl_tracks = before + shuffled + after
self.version += 1 self._increase_version()
def slice(self, start, end): def slice(self, start, end):
""" """

View File

@ -56,10 +56,11 @@ def handle_request(pattern, auth_required=True):
if match is not None: if match is not None:
mpd_commands.add( mpd_commands.add(
MpdCommand(name=match.group(), auth_required=auth_required)) 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' % ( raise ValueError('Tried to redefine handler for %s with %s' % (
pattern, func)) pattern, func))
request_handlers[pattern] = func request_handlers[compiled_pattern] = func
func.__doc__ = ' - *Pattern:* ``%s``\n\n%s' % ( func.__doc__ = ' - *Pattern:* ``%s``\n\n%s' % (
pattern, func.__doc__ or '') pattern, func.__doc__ or '')
return func return func

View File

@ -24,7 +24,7 @@ def add(context, uri):
return return
tracks = context.core.library.lookup(uri).get() tracks = context.core.library.lookup(uri).get()
if tracks: if tracks:
context.core.tracklist.append(tracks) context.core.tracklist.add(tracks)
return return
raise MpdNoExistError('directory or file not found', command='add') 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') raise MpdNoExistError('No such song', command='addid')
if songpos and songpos > context.core.tracklist.length.get(): if songpos and songpos > context.core.tracklist.length.get():
raise MpdArgError('Bad song index', command='addid') raise MpdArgError('Bad song index', command='addid')
first_tl_track = None tl_tracks = context.core.tracklist.add(tracks, at_position=songpos).get()
for track in tracks: return ('Id', tl_tracks[0].tlid)
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)
@handle_request(r'^delete "(?P<start>\d+):(?P<end>\d+)*"$') @handle_request(r'^delete "(?P<start>\d+):(?P<end>\d+)*"$')
@ -110,7 +104,8 @@ def deleteid(context, tlid):
Deletes the song ``SONGID`` from the playlist Deletes the song ``SONGID`` from the playlist
""" """
tlid = int(tlid) 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() context.core.playback.next()
tl_tracks = context.core.tracklist.remove(tlid=tlid).get() tl_tracks = context.core.tracklist.remove(tlid=tlid).get()
if not tl_tracks: if not tl_tracks:
@ -237,7 +232,6 @@ def playlistid(context, tlid=None):
@handle_request(r'^playlistinfo$') @handle_request(r'^playlistinfo$')
@handle_request(r'^playlistinfo "-1"$')
@handle_request(r'^playlistinfo "(?P<songpos>-?\d+)"$') @handle_request(r'^playlistinfo "(?P<songpos>-?\d+)"$')
@handle_request(r'^playlistinfo "(?P<start>\d+):(?P<end>\d+)*"$') @handle_request(r'^playlistinfo "(?P<start>\d+):(?P<end>\d+)*"$')
def playlistinfo(context, songpos=None, start=None, end=None): 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 - uses negative indexes, like ``playlistinfo "-1"``, to request
the entire playlist the entire playlist
""" """
if songpos == '-1':
songpos = None
if songpos is not None: if songpos is not None:
songpos = int(songpos) songpos = int(songpos)
tl_track = context.core.tracklist.tl_tracks.get()[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<tag>[^"]+)" "(?P<needle>[^"]+)"$') @handle_request(r'^playlistsearch "(?P<tag>[^"]+)" "(?P<needle>[^"]+)"$')
@handle_request(r'^playlistsearch (?P<tag>\S+) "(?P<needle>[^"]+)"$') @handle_request(r'^playlistsearch (?P<tag>\w+) "(?P<needle>[^"]+)"$')
def playlistsearch(context, tag, needle): def playlistsearch(context, tag, needle):
""" """
*musicpd.org, current playlist section:* *musicpd.org, current playlist section:*
@ -376,7 +372,7 @@ def swap(context, songpos1, songpos2):
del tracks[songpos2] del tracks[songpos2]
tracks.insert(songpos2, song1) tracks.insert(songpos2, song1)
context.core.tracklist.clear() context.core.tracklist.clear()
context.core.tracklist.append(tracks) context.core.tracklist.add(tracks)
@handle_request(r'^swapid "(?P<tlid1>\d+)" "(?P<tlid2>\d+)"$') @handle_request(r'^swapid "(?P<tlid1>\d+)" "(?P<tlid2>\d+)"$')

View File

@ -52,7 +52,7 @@ def count(context, tag, needle):
@handle_request( @handle_request(
r'^find (?P<mpd_query>("?([Aa]lbum|[Aa]rtist|[Dd]ate|[Ff]ile[name]*|' r'^find (?P<mpd_query>("?([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): def find(context, mpd_query):
""" """
*musicpd.org, music database section:* *musicpd.org, music database section:*
@ -250,7 +250,8 @@ def _list_artist(context, query):
tracks = context.core.library.find_exact(**query).get() tracks = context.core.library.find_exact(**query).get()
for track in tracks: for track in tracks:
for artist in track.artists: for artist in track.artists:
artists.add(('Artist', artist.name)) if artist.name:
artists.add(('Artist', artist.name))
return artists return artists
@ -258,7 +259,7 @@ def _list_album(context, query):
albums = set() albums = set()
tracks = context.core.library.find_exact(**query).get() tracks = context.core.library.find_exact(**query).get()
for track in tracks: for track in tracks:
if track.album is not None: if track.album and track.album.name:
albums.add(('Album', track.album.name)) albums.add(('Album', track.album.name))
return albums return albums
@ -267,7 +268,7 @@ def _list_date(context, query):
dates = set() dates = set()
tracks = context.core.library.find_exact(**query).get() tracks = context.core.library.find_exact(**query).get()
for track in tracks: for track in tracks:
if track.date is not None: if track.date:
dates.add(('Date', track.date)) dates.add(('Date', track.date))
return dates return dates
@ -334,7 +335,7 @@ def rescan(context, uri=None):
@handle_request( @handle_request(
r'^search (?P<mpd_query>("?([Aa]lbum|[Aa]rtist|[Dd]ate|[Ff]ile[name]*|' r'^search (?P<mpd_query>("?([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): def search(context, mpd_query):
""" """
*musicpd.org, music database section:* *musicpd.org, music database section:*

View File

@ -329,9 +329,9 @@ def seek(context, songpos, seconds):
- issues ``seek 1 120`` without quotes around the arguments. - 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) playpos(context, songpos)
context.core.playback.seek(int(seconds) * 1000) context.core.playback.seek(int(seconds) * 1000).get()
@handle_request(r'^seekid "(?P<tlid>\d+)" "(?P<seconds>\d+)"$') @handle_request(r'^seekid "(?P<tlid>\d+)" "(?P<seconds>\d+)"$')
@ -343,9 +343,10 @@ def seekid(context, tlid, seconds):
Seeks to the position ``TIME`` (in seconds) of song ``SONGID``. 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) playid(context, tlid)
context.core.playback.seek(int(seconds) * 1000) context.core.playback.seek(int(seconds) * 1000).get()
@handle_request(r'^setvol (?P<volume>[-+]*\d+)$') @handle_request(r'^setvol (?P<volume>[-+]*\d+)$')

View File

@ -1,7 +1,6 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from mopidy.frontends.mpd.protocol import handle_request, mpd_commands from mopidy.frontends.mpd.protocol import handle_request, mpd_commands
from mopidy.frontends.mpd.exceptions import MpdNotImplemented
@handle_request(r'^commands$', auth_required=False) @handle_request(r'^commands$', auth_required=False)
@ -47,8 +46,15 @@ def decoders(context):
mime_type: audio/mpeg mime_type: audio/mpeg
plugin: mpcdec plugin: mpcdec
suffix: mpc 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) @handle_request(r'^notcommands$', auth_required=False)

View File

@ -7,7 +7,7 @@ from mopidy.frontends.mpd.protocol import handle_request
from mopidy.frontends.mpd.translator import playlist_to_mpd_format from mopidy.frontends.mpd.translator import playlist_to_mpd_format
@handle_request(r'^listplaylist (?P<name>\S+)$') @handle_request(r'^listplaylist (?P<name>\w+)$')
@handle_request(r'^listplaylist "(?P<name>[^"]+)"$') @handle_request(r'^listplaylist "(?P<name>[^"]+)"$')
def listplaylist(context, name): def listplaylist(context, name):
""" """
@ -29,7 +29,7 @@ def listplaylist(context, name):
return ['file: %s' % t.uri for t in playlists[0].tracks] return ['file: %s' % t.uri for t in playlists[0].tracks]
@handle_request(r'^listplaylistinfo (?P<name>\S+)$') @handle_request(r'^listplaylistinfo (?P<name>\w+)$')
@handle_request(r'^listplaylistinfo "(?P<name>[^"]+)"$') @handle_request(r'^listplaylistinfo "(?P<name>[^"]+)"$')
def listplaylistinfo(context, name): def listplaylistinfo(context, name):
""" """
@ -70,9 +70,16 @@ def listplaylists(context):
Last-Modified: 2010-02-06T02:10:25Z Last-Modified: 2010-02-06T02:10:25Z
playlist: b playlist: b
Last-Modified: 2010-02-06T02:11:08Z 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 = [] result = []
for playlist in context.core.playlists.playlists.get(): for playlist in context.core.playlists.playlists.get():
if not playlist.name:
continue
result.append(('playlist', playlist.name)) result.append(('playlist', playlist.name))
last_modified = ( last_modified = (
playlist.last_modified or dt.datetime.now()).isoformat() playlist.last_modified or dt.datetime.now()).isoformat()
@ -101,7 +108,7 @@ def load(context, name):
playlists = context.core.playlists.filter(name=name).get() playlists = context.core.playlists.filter(name=name).get()
if not playlists: if not playlists:
raise MpdNoExistError('No such playlist', command='load') 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<name>[^"]+)" "(?P<uri>[^"]+)"$') @handle_request(r'^playlistadd "(?P<name>[^"]+)" "(?P<uri>[^"]+)"$')

View File

@ -281,7 +281,7 @@ class MprisObject(dbus.service.Object):
# is added to the backend. # is added to the backend.
tracks = self.core.library.lookup(uri).get() tracks = self.core.library.lookup(uri).get()
if tracks: 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]) self.core.playback.play(tl_tracks[0])
else: else:
logger.debug('Track with URI "%s" not found in library.', uri) 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(): if not self.get_CanControl():
return False return False
return ( return (
self.core.playback.current_track.get() is not None or self.core.playback.current_tl_track.get() is not None or
self.core.playback.track_at_next.get() is not None) self.core.playback.tl_track_at_next.get() is not None)
def get_CanPause(self): def get_CanPause(self):
if not self.get_CanControl(): if not self.get_CanControl():
@ -449,7 +449,7 @@ class MprisObject(dbus.service.Object):
playlist_uri = self.get_playlist_uri(playlist_id) playlist_uri = self.get_playlist_uri(playlist_id)
playlist = self.core.playlists.lookup(playlist_uri).get() playlist = self.core.playlists.lookup(playlist_uri).get()
if playlist and playlist.tracks: 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]) self.core.playback.play(tl_tracks[0])
@dbus.service.method(dbus_interface=PLAYLISTS_IFACE) @dbus.service.method(dbus_interface=PLAYLISTS_IFACE)

View File

@ -251,3 +251,34 @@ SPOTIFY_PASSWORD = ''
#: #:
#: SPOTIFY_BITRATE = 160 #: SPOTIFY_BITRATE = 160
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

1
requirements/spotify.txt Normal file
View File

@ -0,0 +1 @@
pyspotify >= 1.9, < 1.10

View File

@ -1,10 +1,9 @@
from __future__ import unicode_literals from __future__ import unicode_literals
def populate_playlist(func): def populate_tracklist(func):
def wrapper(self): def wrapper(self):
for track in self.tracks: self.tl_tracks = self.core.tracklist.add(self.tracks)
self.core.tracklist.add(track)
return func(self) return func(self)
wrapper.__name__ = func.__name__ wrapper.__name__ = func.__name__

View File

@ -9,7 +9,7 @@ from mopidy.core import PlaybackState
from mopidy.models import Track from mopidy.models import Track
from tests import unittest 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 # 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): def test_play_with_empty_playlist_return_value(self):
self.assertEqual(self.playback.play(), None) self.assertEqual(self.playback.play(), None)
@populate_playlist @populate_tracklist
def test_play_state(self): def test_play_state(self):
self.assertEqual(self.playback.state, PlaybackState.STOPPED) self.assertEqual(self.playback.state, PlaybackState.STOPPED)
self.playback.play() self.playback.play()
self.assertEqual(self.playback.state, PlaybackState.PLAYING) self.assertEqual(self.playback.state, PlaybackState.PLAYING)
@populate_playlist @populate_tracklist
def test_play_return_value(self): def test_play_return_value(self):
self.assertEqual(self.playback.play(), None) self.assertEqual(self.playback.play(), None)
@populate_playlist @populate_tracklist
def test_play_track_state(self): def test_play_track_state(self):
self.assertEqual(self.playback.state, PlaybackState.STOPPED) self.assertEqual(self.playback.state, PlaybackState.STOPPED)
self.playback.play(self.tracklist.tl_tracks[-1]) self.playback.play(self.tracklist.tl_tracks[-1])
self.assertEqual(self.playback.state, PlaybackState.PLAYING) self.assertEqual(self.playback.state, PlaybackState.PLAYING)
@populate_playlist @populate_tracklist
def test_play_track_return_value(self): def test_play_track_return_value(self):
self.assertEqual(self.playback.play( self.assertEqual(self.playback.play(
self.tracklist.tl_tracks[-1]), None) self.tracklist.tl_tracks[-1]), None)
@populate_playlist @populate_tracklist
def test_play_when_playing(self): def test_play_when_playing(self):
self.playback.play() self.playback.play()
track = self.playback.current_track track = self.playback.current_track
self.playback.play() self.playback.play()
self.assertEqual(track, self.playback.current_track) self.assertEqual(track, self.playback.current_track)
@populate_playlist @populate_tracklist
def test_play_when_paused(self): def test_play_when_paused(self):
self.playback.play() self.playback.play()
track = self.playback.current_track track = self.playback.current_track
@ -77,7 +77,7 @@ class PlaybackControllerTest(object):
self.assertEqual(self.playback.state, PlaybackState.PLAYING) self.assertEqual(self.playback.state, PlaybackState.PLAYING)
self.assertEqual(track, self.playback.current_track) self.assertEqual(track, self.playback.current_track)
@populate_playlist @populate_tracklist
def test_play_when_pause_after_next(self): def test_play_when_pause_after_next(self):
self.playback.play() self.playback.play()
self.playback.next() self.playback.next()
@ -88,17 +88,17 @@ class PlaybackControllerTest(object):
self.assertEqual(self.playback.state, PlaybackState.PLAYING) self.assertEqual(self.playback.state, PlaybackState.PLAYING)
self.assertEqual(track, self.playback.current_track) self.assertEqual(track, self.playback.current_track)
@populate_playlist @populate_tracklist
def test_play_sets_current_track(self): def test_play_sets_current_track(self):
self.playback.play() self.playback.play()
self.assertEqual(self.playback.current_track, self.tracks[0]) self.assertEqual(self.playback.current_track, self.tracks[0])
@populate_playlist @populate_tracklist
def test_play_track_sets_current_track(self): def test_play_track_sets_current_track(self):
self.playback.play(self.tracklist.tl_tracks[-1]) self.playback.play(self.tracklist.tl_tracks[-1])
self.assertEqual(self.playback.current_track, self.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): def test_play_skips_to_next_track_on_failure(self):
# If backend's play() returns False, it is a failure. # If backend's play() returns False, it is a failure.
self.backend.playback.play = lambda track: track != self.tracks[0] 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.assertNotEqual(self.playback.current_track, self.tracks[0])
self.assertEqual(self.playback.current_track, self.tracks[1]) self.assertEqual(self.playback.current_track, self.tracks[1])
@populate_playlist @populate_tracklist
def test_current_track_after_completed_playlist(self): def test_current_track_after_completed_playlist(self):
self.playback.play(self.tracklist.tl_tracks[-1]) self.playback.play(self.tracklist.tl_tracks[-1])
self.playback.on_end_of_track() self.playback.on_end_of_track()
@ -118,14 +118,14 @@ class PlaybackControllerTest(object):
self.assertEqual(self.playback.state, PlaybackState.STOPPED) self.assertEqual(self.playback.state, PlaybackState.STOPPED)
self.assertEqual(self.playback.current_track, None) self.assertEqual(self.playback.current_track, None)
@populate_playlist @populate_tracklist
def test_previous(self): def test_previous(self):
self.playback.play() self.playback.play()
self.playback.next() self.playback.next()
self.playback.previous() self.playback.previous()
self.assertEqual(self.playback.current_track, self.tracks[0]) self.assertEqual(self.playback.current_track, self.tracks[0])
@populate_playlist @populate_tracklist
def test_previous_more(self): def test_previous_more(self):
self.playback.play() # At track 0 self.playback.play() # At track 0
self.playback.next() # At track 1 self.playback.next() # At track 1
@ -133,13 +133,13 @@ class PlaybackControllerTest(object):
self.playback.previous() # At track 1 self.playback.previous() # At track 1
self.assertEqual(self.playback.current_track, self.tracks[1]) self.assertEqual(self.playback.current_track, self.tracks[1])
@populate_playlist @populate_tracklist
def test_previous_return_value(self): def test_previous_return_value(self):
self.playback.play() self.playback.play()
self.playback.next() self.playback.next()
self.assertEqual(self.playback.previous(), None) self.assertEqual(self.playback.previous(), None)
@populate_playlist @populate_tracklist
def test_previous_does_not_trigger_playback(self): def test_previous_does_not_trigger_playback(self):
self.playback.play() self.playback.play()
self.playback.next() self.playback.next()
@ -147,7 +147,7 @@ class PlaybackControllerTest(object):
self.playback.previous() self.playback.previous()
self.assertEqual(self.playback.state, PlaybackState.STOPPED) self.assertEqual(self.playback.state, PlaybackState.STOPPED)
@populate_playlist @populate_tracklist
def test_previous_at_start_of_playlist(self): def test_previous_at_start_of_playlist(self):
self.playback.previous() self.playback.previous()
self.assertEqual(self.playback.state, PlaybackState.STOPPED) 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.state, PlaybackState.STOPPED)
self.assertEqual(self.playback.current_track, None) self.assertEqual(self.playback.current_track, None)
@populate_playlist @populate_tracklist
def test_previous_skips_to_previous_track_on_failure(self): def test_previous_skips_to_previous_track_on_failure(self):
# If backend's play() returns False, it is a failure. # If backend's play() returns False, it is a failure.
self.backend.playback.play = lambda track: track != self.tracks[1] 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.assertNotEqual(self.playback.current_track, self.tracks[1])
self.assertEqual(self.playback.current_track, self.tracks[0]) self.assertEqual(self.playback.current_track, self.tracks[0])
@populate_playlist @populate_tracklist
def test_next(self): def test_next(self):
self.playback.play() self.playback.play()
@ -181,17 +181,17 @@ class PlaybackControllerTest(object):
self.playback.tracklist_position, old_position + 1) self.playback.tracklist_position, old_position + 1)
self.assertNotEqual(self.playback.current_track.uri, old_uri) self.assertNotEqual(self.playback.current_track.uri, old_uri)
@populate_playlist @populate_tracklist
def test_next_return_value(self): def test_next_return_value(self):
self.playback.play() self.playback.play()
self.assertEqual(self.playback.next(), None) self.assertEqual(self.playback.next(), None)
@populate_playlist @populate_tracklist
def test_next_does_not_trigger_playback(self): def test_next_does_not_trigger_playback(self):
self.playback.next() self.playback.next()
self.assertEqual(self.playback.state, PlaybackState.STOPPED) self.assertEqual(self.playback.state, PlaybackState.STOPPED)
@populate_playlist @populate_tracklist
def test_next_at_end_of_playlist(self): def test_next_at_end_of_playlist(self):
self.playback.play() self.playback.play()
@ -204,7 +204,7 @@ class PlaybackControllerTest(object):
self.assertEqual(self.playback.state, PlaybackState.STOPPED) self.assertEqual(self.playback.state, PlaybackState.STOPPED)
@populate_playlist @populate_tracklist
def test_next_until_end_of_playlist_and_play_from_start(self): def test_next_until_end_of_playlist_and_play_from_start(self):
self.playback.play() self.playback.play()
@ -222,7 +222,7 @@ class PlaybackControllerTest(object):
self.playback.next() self.playback.next()
self.assertEqual(self.playback.state, PlaybackState.STOPPED) self.assertEqual(self.playback.state, PlaybackState.STOPPED)
@populate_playlist @populate_tracklist
def test_next_skips_to_next_track_on_failure(self): def test_next_skips_to_next_track_on_failure(self):
# If backend's play() returns False, it is a failure. # If backend's play() returns False, it is a failure.
self.backend.playback.play = lambda track: track != self.tracks[1] 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.assertNotEqual(self.playback.current_track, self.tracks[1])
self.assertEqual(self.playback.current_track, self.tracks[2]) self.assertEqual(self.playback.current_track, self.tracks[2])
@populate_playlist @populate_tracklist
def test_next_track_before_play(self): 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): def test_next_track_during_play(self):
self.playback.play() 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): def test_next_track_after_previous(self):
self.playback.play() self.playback.play()
self.playback.next() self.playback.next()
self.playback.previous() 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): 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): def test_next_track_at_end_of_playlist(self):
self.playback.play() self.playback.play()
for _ in self.tracklist.tl_tracks[1:]: for _ in self.tracklist.tl_tracks[1:]:
self.playback.next() 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): def test_next_track_at_end_of_playlist_with_repeat(self):
self.playback.repeat = True self.playback.repeat = True
self.playback.play() self.playback.play()
for _ in self.tracks[1:]: for _ in self.tracks[1:]:
self.playback.next() 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): def test_next_track_with_random(self):
random.seed(1) random.seed(1)
self.playback.random = True 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): def test_next_with_consume(self):
self.playback.consume = True self.playback.consume = True
self.playback.play() self.playback.play()
self.playback.next() self.playback.next()
self.assertIn(self.tracks[0], self.tracklist.tracks) self.assertIn(self.tracks[0], self.tracklist.tracks)
@populate_playlist @populate_tracklist
def test_next_with_single_and_repeat(self): def test_next_with_single_and_repeat(self):
self.playback.single = True self.playback.single = True
self.playback.repeat = True self.playback.repeat = True
@ -287,7 +287,7 @@ class PlaybackControllerTest(object):
self.playback.next() self.playback.next()
self.assertEqual(self.playback.current_track, self.tracks[1]) self.assertEqual(self.playback.current_track, self.tracks[1])
@populate_playlist @populate_tracklist
def test_next_with_random(self): def test_next_with_random(self):
# FIXME feels very fragile # FIXME feels very fragile
random.seed(1) random.seed(1)
@ -296,15 +296,15 @@ class PlaybackControllerTest(object):
self.playback.next() self.playback.next()
self.assertEqual(self.playback.current_track, self.tracks[1]) self.assertEqual(self.playback.current_track, self.tracks[1])
@populate_playlist @populate_tracklist
def test_next_track_with_random_after_append_playlist(self): def test_next_track_with_random_after_append_playlist(self):
random.seed(1) random.seed(1)
self.playback.random = True 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])
self.tracklist.append(self.tracks[:1]) self.tracklist.add(self.tracks[:1])
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(self): def test_end_of_track(self):
self.playback.play() self.playback.play()
@ -317,17 +317,17 @@ class PlaybackControllerTest(object):
self.playback.tracklist_position, old_position + 1) self.playback.tracklist_position, old_position + 1)
self.assertNotEqual(self.playback.current_track.uri, old_uri) self.assertNotEqual(self.playback.current_track.uri, old_uri)
@populate_playlist @populate_tracklist
def test_end_of_track_return_value(self): def test_end_of_track_return_value(self):
self.playback.play() self.playback.play()
self.assertEqual(self.playback.on_end_of_track(), None) self.assertEqual(self.playback.on_end_of_track(), None)
@populate_playlist @populate_tracklist
def test_end_of_track_does_not_trigger_playback(self): def test_end_of_track_does_not_trigger_playback(self):
self.playback.on_end_of_track() self.playback.on_end_of_track()
self.assertEqual(self.playback.state, PlaybackState.STOPPED) self.assertEqual(self.playback.state, PlaybackState.STOPPED)
@populate_playlist @populate_tracklist
def test_end_of_track_at_end_of_playlist(self): def test_end_of_track_at_end_of_playlist(self):
self.playback.play() self.playback.play()
@ -340,7 +340,7 @@ class PlaybackControllerTest(object):
self.assertEqual(self.playback.state, PlaybackState.STOPPED) 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): def test_end_of_track_until_end_of_playlist_and_play_from_start(self):
self.playback.play() self.playback.play()
@ -358,7 +358,7 @@ class PlaybackControllerTest(object):
self.playback.on_end_of_track() self.playback.on_end_of_track()
self.assertEqual(self.playback.state, PlaybackState.STOPPED) self.assertEqual(self.playback.state, PlaybackState.STOPPED)
@populate_playlist @populate_tracklist
def test_end_of_track_skips_to_next_track_on_failure(self): def test_end_of_track_skips_to_next_track_on_failure(self):
# If backend's play() returns False, it is a failure. # If backend's play() returns False, it is a failure.
self.backend.playback.play = lambda track: track != self.tracks[1] 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.assertNotEqual(self.playback.current_track, self.tracks[1])
self.assertEqual(self.playback.current_track, self.tracks[2]) self.assertEqual(self.playback.current_track, self.tracks[2])
@populate_playlist @populate_tracklist
def test_end_of_track_track_before_play(self): 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): def test_end_of_track_track_during_play(self):
self.playback.play() 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): def test_end_of_track_track_after_previous(self):
self.playback.play() self.playback.play()
self.playback.on_end_of_track() self.playback.on_end_of_track()
self.playback.previous() 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): 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): def test_end_of_track_track_at_end_of_playlist(self):
self.playback.play() self.playback.play()
for _ in self.tracklist.tl_tracks[1:]: for _ in self.tracklist.tl_tracks[1:]:
self.playback.on_end_of_track() 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): def test_end_of_track_track_at_end_of_playlist_with_repeat(self):
self.playback.repeat = True self.playback.repeat = True
self.playback.play() self.playback.play()
for _ in self.tracks[1:]: for _ in self.tracks[1:]:
self.playback.on_end_of_track() 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): def test_end_of_track_track_with_random(self):
random.seed(1) random.seed(1)
self.playback.random = True 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): def test_end_of_track_with_consume(self):
self.playback.consume = True self.playback.consume = True
self.playback.play() self.playback.play()
self.playback.on_end_of_track() self.playback.on_end_of_track()
self.assertNotIn(self.tracks[0], self.tracklist.tracks) self.assertNotIn(self.tracks[0], self.tracklist.tracks)
@populate_playlist @populate_tracklist
def test_end_of_track_with_random(self): def test_end_of_track_with_random(self):
# FIXME feels very fragile # FIXME feels very fragile
random.seed(1) random.seed(1)
@ -424,87 +424,89 @@ class PlaybackControllerTest(object):
self.playback.on_end_of_track() self.playback.on_end_of_track()
self.assertEqual(self.playback.current_track, self.tracks[1]) 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): def test_end_of_track_track_with_random_after_append_playlist(self):
random.seed(1) random.seed(1)
self.playback.random = True 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])
self.tracklist.append(self.tracks[:1]) self.tracklist.add(self.tracks[:1])
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_previous_track_before_play(self): 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): def test_previous_track_after_play(self):
self.playback.play() 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): def test_previous_track_after_next(self):
self.playback.play() self.playback.play()
self.playback.next() 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): def test_previous_track_after_previous(self):
self.playback.play() # At track 0 self.playback.play() # At track 0
self.playback.next() # At track 1 self.playback.next() # At track 1
self.playback.next() # At track 2 self.playback.next() # At track 2
self.playback.previous() # At track 1 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): 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): def test_previous_track_with_consume(self):
self.playback.consume = True self.playback.consume = True
for _ in self.tracks: for _ in self.tracks:
self.playback.next() self.playback.next()
self.assertEqual( 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): def test_previous_track_with_random(self):
self.playback.random = True self.playback.random = True
for _ in self.tracks: for _ in self.tracks:
self.playback.next() self.playback.next()
self.assertEqual( 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): def test_initial_current_track(self):
self.assertEqual(self.playback.current_track, None) self.assertEqual(self.playback.current_track, None)
@populate_playlist @populate_tracklist
def test_current_track_during_play(self): def test_current_track_during_play(self):
self.playback.play() self.playback.play()
self.assertEqual(self.playback.current_track, self.tracks[0]) self.assertEqual(self.playback.current_track, self.tracks[0])
@populate_playlist @populate_tracklist
def test_current_track_after_next(self): def test_current_track_after_next(self):
self.playback.play() self.playback.play()
self.playback.next() self.playback.next()
self.assertEqual(self.playback.current_track, self.tracks[1]) self.assertEqual(self.playback.current_track, self.tracks[1])
@populate_playlist @populate_tracklist
def test_initial_tracklist_position(self): def test_initial_tracklist_position(self):
self.assertEqual(self.playback.tracklist_position, None) self.assertEqual(self.playback.tracklist_position, None)
@populate_playlist @populate_tracklist
def test_tracklist_position_during_play(self): def test_tracklist_position_during_play(self):
self.playback.play() self.playback.play()
self.assertEqual(self.playback.tracklist_position, 0) self.assertEqual(self.playback.tracklist_position, 0)
@populate_playlist @populate_tracklist
def test_tracklist_position_after_next(self): def test_tracklist_position_after_next(self):
self.playback.play() self.playback.play()
self.playback.next() self.playback.next()
self.assertEqual(self.playback.tracklist_position, 1) self.assertEqual(self.playback.tracklist_position, 1)
@populate_playlist @populate_tracklist
def test_tracklist_position_at_end_of_playlist(self): def test_tracklist_position_at_end_of_playlist(self):
self.playback.play(self.tracklist.tl_tracks[-1]) self.playback.play(self.tracklist.tl_tracks[-1])
self.playback.on_end_of_track() self.playback.on_end_of_track()
@ -519,12 +521,12 @@ class PlaybackControllerTest(object):
wrapper.called = False wrapper.called = False
self.playback.on_tracklist_change = wrapper self.playback.on_tracklist_change = wrapper
self.tracklist.append([Track()]) self.tracklist.add([Track()])
self.assert_(wrapper.called) self.assert_(wrapper.called)
@unittest.SkipTest # Blocks for 10ms @unittest.SkipTest # Blocks for 10ms
@populate_playlist @populate_tracklist
def test_end_of_track_callback_gets_called(self): def test_end_of_track_callback_gets_called(self):
self.playback.play() self.playback.play()
result = self.playback.seek(self.tracks[0].length - 10) result = self.playback.seek(self.tracks[0].length - 10)
@ -532,78 +534,78 @@ class PlaybackControllerTest(object):
message = self.core_queue.get(True, 1) message = self.core_queue.get(True, 1)
self.assertEqual('end_of_track', message['command']) self.assertEqual('end_of_track', message['command'])
@populate_playlist @populate_tracklist
def test_on_tracklist_change_when_playing(self): def test_on_tracklist_change_when_playing(self):
self.playback.play() self.playback.play()
current_track = self.playback.current_track 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.state, PlaybackState.PLAYING)
self.assertEqual(self.playback.current_track, current_track) self.assertEqual(self.playback.current_track, current_track)
@populate_playlist @populate_tracklist
def test_on_tracklist_change_when_stopped(self): 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.state, PlaybackState.STOPPED)
self.assertEqual(self.playback.current_track, None) self.assertEqual(self.playback.current_track, None)
@populate_playlist @populate_tracklist
def test_on_tracklist_change_when_paused(self): def test_on_tracklist_change_when_paused(self):
self.playback.play() self.playback.play()
self.playback.pause() self.playback.pause()
current_track = self.playback.current_track 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.state, PlaybackState.PAUSED)
self.assertEqual(self.playback.current_track, current_track) self.assertEqual(self.playback.current_track, current_track)
@populate_playlist @populate_tracklist
def test_pause_when_stopped(self): def test_pause_when_stopped(self):
self.playback.pause() self.playback.pause()
self.assertEqual(self.playback.state, PlaybackState.PAUSED) self.assertEqual(self.playback.state, PlaybackState.PAUSED)
@populate_playlist @populate_tracklist
def test_pause_when_playing(self): def test_pause_when_playing(self):
self.playback.play() self.playback.play()
self.playback.pause() self.playback.pause()
self.assertEqual(self.playback.state, PlaybackState.PAUSED) self.assertEqual(self.playback.state, PlaybackState.PAUSED)
@populate_playlist @populate_tracklist
def test_pause_when_paused(self): def test_pause_when_paused(self):
self.playback.play() self.playback.play()
self.playback.pause() self.playback.pause()
self.playback.pause() self.playback.pause()
self.assertEqual(self.playback.state, PlaybackState.PAUSED) self.assertEqual(self.playback.state, PlaybackState.PAUSED)
@populate_playlist @populate_tracklist
def test_pause_return_value(self): def test_pause_return_value(self):
self.playback.play() self.playback.play()
self.assertEqual(self.playback.pause(), None) self.assertEqual(self.playback.pause(), None)
@populate_playlist @populate_tracklist
def test_resume_when_stopped(self): def test_resume_when_stopped(self):
self.playback.resume() self.playback.resume()
self.assertEqual(self.playback.state, PlaybackState.STOPPED) self.assertEqual(self.playback.state, PlaybackState.STOPPED)
@populate_playlist @populate_tracklist
def test_resume_when_playing(self): def test_resume_when_playing(self):
self.playback.play() self.playback.play()
self.playback.resume() self.playback.resume()
self.assertEqual(self.playback.state, PlaybackState.PLAYING) self.assertEqual(self.playback.state, PlaybackState.PLAYING)
@populate_playlist @populate_tracklist
def test_resume_when_paused(self): def test_resume_when_paused(self):
self.playback.play() self.playback.play()
self.playback.pause() self.playback.pause()
self.playback.resume() self.playback.resume()
self.assertEqual(self.playback.state, PlaybackState.PLAYING) self.assertEqual(self.playback.state, PlaybackState.PLAYING)
@populate_playlist @populate_tracklist
def test_resume_return_value(self): def test_resume_return_value(self):
self.playback.play() self.playback.play()
self.playback.pause() self.playback.pause()
self.assertEqual(self.playback.resume(), None) self.assertEqual(self.playback.resume(), None)
@unittest.SkipTest # Uses sleep and might not work with LocalBackend @unittest.SkipTest # Uses sleep and might not work with LocalBackend
@populate_playlist @populate_tracklist
def test_resume_continues_from_right_position(self): def test_resume_continues_from_right_position(self):
self.playback.play() self.playback.play()
time.sleep(0.2) time.sleep(0.2)
@ -611,12 +613,12 @@ class PlaybackControllerTest(object):
self.playback.resume() self.playback.resume()
self.assertNotEqual(self.playback.time_position, 0) self.assertNotEqual(self.playback.time_position, 0)
@populate_playlist @populate_tracklist
def test_seek_when_stopped(self): def test_seek_when_stopped(self):
result = self.playback.seek(1000) result = self.playback.seek(1000)
self.assert_(result, 'Seek return value was %s' % result) self.assert_(result, 'Seek return value was %s' % result)
@populate_playlist @populate_tracklist
def test_seek_when_stopped_updates_position(self): def test_seek_when_stopped_updates_position(self):
self.playback.seek(1000) self.playback.seek(1000)
position = self.playback.time_position position = self.playback.time_position
@ -629,18 +631,18 @@ class PlaybackControllerTest(object):
self.playback.seek(0) self.playback.seek(0)
self.assertEqual(self.playback.state, PlaybackState.STOPPED) self.assertEqual(self.playback.state, PlaybackState.STOPPED)
@populate_playlist @populate_tracklist
def test_seek_when_stopped_triggers_play(self): def test_seek_when_stopped_triggers_play(self):
self.playback.seek(0) self.playback.seek(0)
self.assertEqual(self.playback.state, PlaybackState.PLAYING) self.assertEqual(self.playback.state, PlaybackState.PLAYING)
@populate_playlist @populate_tracklist
def test_seek_when_playing(self): def test_seek_when_playing(self):
self.playback.play() self.playback.play()
result = self.playback.seek(self.tracks[0].length - 1000) result = self.playback.seek(self.tracks[0].length - 1000)
self.assert_(result, 'Seek return value was %s' % result) self.assert_(result, 'Seek return value was %s' % result)
@populate_playlist @populate_tracklist
def test_seek_when_playing_updates_position(self): def test_seek_when_playing_updates_position(self):
length = self.tracklist.tracks[0].length length = self.tracklist.tracks[0].length
self.playback.play() self.playback.play()
@ -648,14 +650,14 @@ class PlaybackControllerTest(object):
position = self.playback.time_position position = self.playback.time_position
self.assertGreaterEqual(position, length - 1010) self.assertGreaterEqual(position, length - 1010)
@populate_playlist @populate_tracklist
def test_seek_when_paused(self): def test_seek_when_paused(self):
self.playback.play() self.playback.play()
self.playback.pause() self.playback.pause()
result = self.playback.seek(self.tracks[0].length - 1000) result = self.playback.seek(self.tracks[0].length - 1000)
self.assert_(result, 'Seek return value was %s' % result) self.assert_(result, 'Seek return value was %s' % result)
@populate_playlist @populate_tracklist
def test_seek_when_paused_updates_position(self): def test_seek_when_paused_updates_position(self):
length = self.tracklist.tracks[0].length length = self.tracklist.tracks[0].length
self.playback.play() self.playback.play()
@ -664,7 +666,7 @@ class PlaybackControllerTest(object):
position = self.playback.time_position position = self.playback.time_position
self.assertGreaterEqual(position, length - 1010) self.assertGreaterEqual(position, length - 1010)
@populate_playlist @populate_tracklist
def test_seek_when_paused_triggers_play(self): def test_seek_when_paused_triggers_play(self):
self.playback.play() self.playback.play()
self.playback.pause() self.playback.pause()
@ -672,34 +674,34 @@ class PlaybackControllerTest(object):
self.assertEqual(self.playback.state, PlaybackState.PLAYING) self.assertEqual(self.playback.state, PlaybackState.PLAYING)
@unittest.SkipTest @unittest.SkipTest
@populate_playlist @populate_tracklist
def test_seek_beyond_end_of_song(self): def test_seek_beyond_end_of_song(self):
# FIXME need to decide return value # FIXME need to decide return value
self.playback.play() self.playback.play()
result = self.playback.seek(self.tracks[0].length * 100) result = self.playback.seek(self.tracks[0].length * 100)
self.assert_(not result, 'Seek return value was %s' % result) 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): def test_seek_beyond_end_of_song_jumps_to_next_song(self):
self.playback.play() self.playback.play()
self.playback.seek(self.tracks[0].length * 100) self.playback.seek(self.tracks[0].length * 100)
self.assertEqual(self.playback.current_track, self.tracks[1]) self.assertEqual(self.playback.current_track, self.tracks[1])
@populate_playlist @populate_tracklist
def test_seek_beyond_end_of_song_for_last_track(self): def test_seek_beyond_end_of_song_for_last_track(self):
self.playback.play(self.tracklist.tl_tracks[-1]) self.playback.play(self.tracklist.tl_tracks[-1])
self.playback.seek(self.tracklist.tracks[-1].length * 100) self.playback.seek(self.tracklist.tracks[-1].length * 100)
self.assertEqual(self.playback.state, PlaybackState.STOPPED) self.assertEqual(self.playback.state, PlaybackState.STOPPED)
@unittest.SkipTest @unittest.SkipTest
@populate_playlist @populate_tracklist
def test_seek_beyond_start_of_song(self): def test_seek_beyond_start_of_song(self):
# FIXME need to decide return value # FIXME need to decide return value
self.playback.play() self.playback.play()
result = self.playback.seek(-1000) result = self.playback.seek(-1000)
self.assert_(not result, 'Seek return value was %s' % result) self.assert_(not result, 'Seek return value was %s' % result)
@populate_playlist @populate_tracklist
def test_seek_beyond_start_of_song_update_postion(self): def test_seek_beyond_start_of_song_update_postion(self):
self.playback.play() self.playback.play()
self.playback.seek(-1000) self.playback.seek(-1000)
@ -707,18 +709,18 @@ class PlaybackControllerTest(object):
self.assertGreaterEqual(position, 0) self.assertGreaterEqual(position, 0)
self.assertEqual(self.playback.state, PlaybackState.PLAYING) self.assertEqual(self.playback.state, PlaybackState.PLAYING)
@populate_playlist @populate_tracklist
def test_stop_when_stopped(self): def test_stop_when_stopped(self):
self.playback.stop() self.playback.stop()
self.assertEqual(self.playback.state, PlaybackState.STOPPED) self.assertEqual(self.playback.state, PlaybackState.STOPPED)
@populate_playlist @populate_tracklist
def test_stop_when_playing(self): def test_stop_when_playing(self):
self.playback.play() self.playback.play()
self.playback.stop() self.playback.stop()
self.assertEqual(self.playback.state, PlaybackState.STOPPED) self.assertEqual(self.playback.state, PlaybackState.STOPPED)
@populate_playlist @populate_tracklist
def test_stop_when_paused(self): def test_stop_when_paused(self):
self.playback.play() self.playback.play()
self.playback.pause() self.playback.pause()
@ -736,7 +738,7 @@ class PlaybackControllerTest(object):
self.assertEqual(self.playback.time_position, 0) self.assertEqual(self.playback.time_position, 0)
@populate_playlist @populate_tracklist
def test_time_position_when_stopped_with_playlist(self): def test_time_position_when_stopped_with_playlist(self):
future = mock.Mock() future = mock.Mock()
future.get = mock.Mock(return_value=0) future.get = mock.Mock(return_value=0)
@ -745,7 +747,7 @@ class PlaybackControllerTest(object):
self.assertEqual(self.playback.time_position, 0) self.assertEqual(self.playback.time_position, 0)
@unittest.SkipTest # Uses sleep and does might not work with LocalBackend @unittest.SkipTest # Uses sleep and does might not work with LocalBackend
@populate_playlist @populate_tracklist
def test_time_position_when_playing(self): def test_time_position_when_playing(self):
self.playback.play() self.playback.play()
first = self.playback.time_position first = self.playback.time_position
@ -754,7 +756,7 @@ class PlaybackControllerTest(object):
self.assertGreater(second, first) self.assertGreater(second, first)
@unittest.SkipTest # Uses sleep @unittest.SkipTest # Uses sleep
@populate_playlist @populate_tracklist
def test_time_position_when_paused(self): def test_time_position_when_paused(self):
self.playback.play() self.playback.play()
time.sleep(0.2) time.sleep(0.2)
@ -764,13 +766,13 @@ class PlaybackControllerTest(object):
second = self.playback.time_position second = self.playback.time_position
self.assertEqual(first, second) self.assertEqual(first, second)
@populate_playlist @populate_tracklist
def test_play_with_consume(self): def test_play_with_consume(self):
self.playback.consume = True self.playback.consume = True
self.playback.play() self.playback.play()
self.assertEqual(self.playback.current_track, self.tracks[0]) 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): def test_playlist_is_empty_after_all_tracks_are_played_with_consume(self):
self.playback.consume = True self.playback.consume = True
self.playback.play() self.playback.play()
@ -778,14 +780,14 @@ class PlaybackControllerTest(object):
self.playback.on_end_of_track() self.playback.on_end_of_track()
self.assertEqual(len(self.tracklist.tracks), 0) self.assertEqual(len(self.tracklist.tracks), 0)
@populate_playlist @populate_tracklist
def test_play_with_random(self): def test_play_with_random(self):
random.seed(1) random.seed(1)
self.playback.random = True self.playback.random = True
self.playback.play() self.playback.play()
self.assertEqual(self.playback.current_track, self.tracks[2]) self.assertEqual(self.playback.current_track, self.tracks[2])
@populate_playlist @populate_tracklist
def test_previous_with_random(self): def test_previous_with_random(self):
random.seed(1) random.seed(1)
self.playback.random = True self.playback.random = True
@ -795,13 +797,13 @@ class PlaybackControllerTest(object):
self.playback.previous() self.playback.previous()
self.assertEqual(self.playback.current_track, current_track) self.assertEqual(self.playback.current_track, current_track)
@populate_playlist @populate_tracklist
def test_end_of_song_starts_next_track(self): def test_end_of_song_starts_next_track(self):
self.playback.play() self.playback.play()
self.playback.on_end_of_track() self.playback.on_end_of_track()
self.assertEqual(self.playback.current_track, self.tracks[1]) 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): def test_end_of_song_with_single_and_repeat_starts_same(self):
self.playback.single = True self.playback.single = True
self.playback.repeat = True self.playback.repeat = True
@ -809,7 +811,7 @@ class PlaybackControllerTest(object):
self.playback.on_end_of_track() self.playback.on_end_of_track()
self.assertEqual(self.playback.current_track, self.tracks[0]) self.assertEqual(self.playback.current_track, self.tracks[0])
@populate_playlist @populate_tracklist
def test_end_of_playlist_stops(self): def test_end_of_playlist_stops(self):
self.playback.play(self.tracklist.tl_tracks[-1]) self.playback.play(self.tracklist.tl_tracks[-1])
self.playback.on_end_of_track() self.playback.on_end_of_track()
@ -824,34 +826,34 @@ class PlaybackControllerTest(object):
def test_consume_off_by_default(self): def test_consume_off_by_default(self):
self.assertEqual(self.playback.consume, False) self.assertEqual(self.playback.consume, False)
@populate_playlist @populate_tracklist
def test_random_until_end_of_playlist(self): def test_random_until_end_of_playlist(self):
self.playback.random = True self.playback.random = True
self.playback.play() self.playback.play()
for _ in self.tracks[1:]: for _ in self.tracks[1:]:
self.playback.next() 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): def test_random_until_end_of_playlist_and_play_from_start(self):
self.playback.repeat = True self.playback.repeat = True
for _ in self.tracks: for _ in self.tracks:
self.playback.next() 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.assertEqual(self.playback.state, PlaybackState.STOPPED)
self.playback.play() self.playback.play()
self.assertEqual(self.playback.state, PlaybackState.PLAYING) self.assertEqual(self.playback.state, PlaybackState.PLAYING)
@populate_playlist @populate_tracklist
def test_random_until_end_of_playlist_with_repeat(self): def test_random_until_end_of_playlist_with_repeat(self):
self.playback.repeat = True self.playback.repeat = True
self.playback.random = True self.playback.random = True
self.playback.play() self.playback.play()
for _ in self.tracks: for _ in self.tracks:
self.playback.next() 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): def test_played_track_during_random_not_played_again(self):
self.playback.random = True self.playback.random = True
self.playback.play() self.playback.play()
@ -861,7 +863,7 @@ class PlaybackControllerTest(object):
played.append(self.playback.current_track) played.append(self.playback.current_track)
self.playback.next() self.playback.next()
@populate_playlist @populate_tracklist
def test_playing_track_that_isnt_in_playlist(self): def test_playing_track_that_isnt_in_playlist(self):
test = lambda: self.playback.play((17, Track())) test = lambda: self.playback.play((17, Track()))
self.assertRaises(AssertionError, test) self.assertRaises(AssertionError, test)

View File

@ -9,7 +9,7 @@ from mopidy import audio, core
from mopidy.core import PlaybackState from mopidy.core import PlaybackState
from mopidy.models import TlTrack, Playlist, Track from mopidy.models import TlTrack, Playlist, Track
from tests.backends.base import populate_playlist from tests.backends.base import populate_tracklist
class TracklistControllerTest(object): class TracklistControllerTest(object):
@ -30,54 +30,56 @@ class TracklistControllerTest(object):
def test_length(self): def test_length(self):
self.assertEqual(0, len(self.controller.tl_tracks)) self.assertEqual(0, len(self.controller.tl_tracks))
self.assertEqual(0, self.controller.length) 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, len(self.controller.tl_tracks))
self.assertEqual(3, self.controller.length) self.assertEqual(3, self.controller.length)
def test_add(self): def test_add(self):
for track in self.tracks: 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(track, self.controller.tracks[-1])
self.assertEqual(tl_track, self.controller.tl_tracks[-1]) self.assertEqual(tl_tracks[0], self.controller.tl_tracks[-1])
self.assertEqual(track, tl_track.track) self.assertEqual(track, tl_tracks[0].track)
def test_add_at_position(self): def test_add_at_position(self):
for track in self.tracks[:-1]: 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(track, self.controller.tracks[0])
self.assertEqual(tl_track, self.controller.tl_tracks[0]) self.assertEqual(tl_tracks[0], self.controller.tl_tracks[0])
self.assertEqual(track, tl_track.track) self.assertEqual(track, tl_tracks[0].track)
@populate_playlist @populate_tracklist
def test_add_at_position_outside_of_playlist(self): def test_add_at_position_outside_of_playlist(self):
test = lambda: self.controller.add( for track in self.tracks:
self.tracks[0], len(self.tracks) + 2) tl_tracks = self.controller.add([track], len(self.tracks) + 2)
self.assertRaises(AssertionError, test) 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): def test_filter_by_tlid(self):
tl_track = self.controller.tl_tracks[1] tl_track = self.controller.tl_tracks[1]
self.assertEqual( self.assertEqual(
[tl_track], self.controller.filter(tlid=tl_track.tlid)) [tl_track], self.controller.filter(tlid=tl_track.tlid))
@populate_playlist @populate_tracklist
def test_filter_by_uri(self): def test_filter_by_uri(self):
tl_track = self.controller.tl_tracks[1] tl_track = self.controller.tl_tracks[1]
self.assertEqual( self.assertEqual(
[tl_track], self.controller.filter(uri=tl_track.track.uri)) [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): def test_filter_by_uri_returns_nothing_for_invalid_uri(self):
self.assertEqual([], self.controller.filter(uri='foobar')) self.assertEqual([], self.controller.filter(uri='foobar'))
def test_filter_by_uri_returns_single_match(self): def test_filter_by_uri_returns_single_match(self):
track = Track(uri='a') 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) self.assertEqual(track, self.controller.filter(uri='a')[0].track)
def test_filter_by_uri_returns_multiple_matches(self): def test_filter_by_uri_returns_multiple_matches(self):
track = Track(uri='a') 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') tl_tracks = self.controller.filter(uri='a')
self.assertEqual(track, tl_tracks[0].track) self.assertEqual(track, tl_tracks[0].track)
self.assertEqual(track, tl_tracks[1].track) self.assertEqual(track, tl_tracks[1].track)
@ -91,7 +93,7 @@ class TracklistControllerTest(object):
track1 = Track(uri='a', name='x') track1 = Track(uri='a', name='x')
track2 = Track(uri='b', name='x') track2 = Track(uri='b', name='x')
track3 = Track(uri='b', name='y') track3 = Track(uri='b', name='y')
self.controller.append([track1, track2, track3]) self.controller.add([track1, track2, track3])
self.assertEqual( self.assertEqual(
track1, self.controller.filter(uri='a', name='x')[0].track) track1, self.controller.filter(uri='a', name='x')[0].track)
self.assertEqual( self.assertEqual(
@ -103,10 +105,10 @@ class TracklistControllerTest(object):
track1 = Track() track1 = Track()
track2 = Track(uri='b') track2 = Track(uri='b')
track3 = Track() track3 = Track()
self.controller.append([track1, track2, track3]) self.controller.add([track1, track2, track3])
self.assertEqual(track2, self.controller.filter(uri='b')[0].track) self.assertEqual(track2, self.controller.filter(uri='b')[0].track)
@populate_playlist @populate_tracklist
def test_clear(self): def test_clear(self):
self.controller.clear() self.controller.clear()
self.assertEqual(len(self.controller.tracks), 0) self.assertEqual(len(self.controller.tracks), 0)
@ -115,49 +117,49 @@ class TracklistControllerTest(object):
self.controller.clear() self.controller.clear()
self.assertEqual(len(self.controller.tracks), 0) self.assertEqual(len(self.controller.tracks), 0)
@populate_playlist @populate_tracklist
def test_clear_when_playing(self): def test_clear_when_playing(self):
self.playback.play() self.playback.play()
self.assertEqual(self.playback.state, PlaybackState.PLAYING) self.assertEqual(self.playback.state, PlaybackState.PLAYING)
self.controller.clear() self.controller.clear()
self.assertEqual(self.playback.state, PlaybackState.STOPPED) self.assertEqual(self.playback.state, PlaybackState.STOPPED)
def test_append_appends_to_the_tracklist(self): def test_add_appends_to_the_tracklist(self):
self.controller.append([Track(uri='a'), Track(uri='b')]) self.controller.add([Track(uri='a'), Track(uri='b')])
self.assertEqual(len(self.controller.tracks), 2) 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(len(self.controller.tracks), 4)
self.assertEqual(self.controller.tracks[0].uri, 'a') self.assertEqual(self.controller.tracks[0].uri, 'a')
self.assertEqual(self.controller.tracks[1].uri, 'b') self.assertEqual(self.controller.tracks[1].uri, 'b')
self.assertEqual(self.controller.tracks[2].uri, 'c') self.assertEqual(self.controller.tracks[2].uri, 'c')
self.assertEqual(self.controller.tracks[3].uri, 'd') 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 version = self.controller.version
self.controller.append([]) self.controller.add([])
self.assertEqual(self.controller.version, version) self.assertEqual(self.controller.version, version)
@populate_playlist @populate_tracklist
def test_append_preserves_playing_state(self): def test_add_preserves_playing_state(self):
self.playback.play() self.playback.play()
track = self.playback.current_track 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.state, PlaybackState.PLAYING)
self.assertEqual(self.playback.current_track, track) self.assertEqual(self.playback.current_track, track)
@populate_playlist @populate_tracklist
def test_append_preserves_stopped_state(self): def test_add_preserves_stopped_state(self):
self.controller.append(self.controller.tracks[1:2]) self.controller.add(self.controller.tracks[1:2])
self.assertEqual(self.playback.state, PlaybackState.STOPPED) self.assertEqual(self.playback.state, PlaybackState.STOPPED)
self.assertEqual(self.playback.current_track, None) self.assertEqual(self.playback.current_track, None)
@populate_playlist @populate_tracklist
def test_append_returns_the_tl_tracks_that_was_added(self): def test_add_returns_the_tl_tracks_that_was_added(self):
tl_tracks = self.controller.append(self.controller.tracks[1:2]) tl_tracks = self.controller.add(self.controller.tracks[1:2])
self.assertEqual(tl_tracks[0].track, self.controller.tracks[1]) self.assertEqual(tl_tracks[0].track, self.controller.tracks[1])
def test_index_returns_index_of_track(self): 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(0, self.controller.index(tl_tracks[0]))
self.assertEquals(1, self.controller.index(tl_tracks[1])) self.assertEquals(1, self.controller.index(tl_tracks[1]))
self.assertEquals(2, self.controller.index(tl_tracks[2])) self.assertEquals(2, self.controller.index(tl_tracks[2]))
@ -166,14 +168,14 @@ class TracklistControllerTest(object):
test = lambda: self.controller.index(TlTrack(0, Track())) test = lambda: self.controller.index(TlTrack(0, Track()))
self.assertRaises(ValueError, test) self.assertRaises(ValueError, test)
@populate_playlist @populate_tracklist
def test_move_single(self): def test_move_single(self):
self.controller.move(0, 0, 2) self.controller.move(0, 0, 2)
tracks = self.controller.tracks tracks = self.controller.tracks
self.assertEqual(tracks[2], self.tracks[0]) self.assertEqual(tracks[2], self.tracks[0])
@populate_playlist @populate_tracklist
def test_move_group(self): def test_move_group(self):
self.controller.move(0, 2, 1) self.controller.move(0, 2, 1)
@ -181,25 +183,25 @@ class TracklistControllerTest(object):
self.assertEqual(tracks[1], self.tracks[0]) self.assertEqual(tracks[1], self.tracks[0])
self.assertEqual(tracks[2], self.tracks[1]) self.assertEqual(tracks[2], self.tracks[1])
@populate_playlist @populate_tracklist
def test_moving_track_outside_of_playlist(self): def test_moving_track_outside_of_playlist(self):
tracks = len(self.controller.tracks) tracks = len(self.controller.tracks)
test = lambda: self.controller.move(0, 0, tracks + 5) test = lambda: self.controller.move(0, 0, tracks + 5)
self.assertRaises(AssertionError, test) self.assertRaises(AssertionError, test)
@populate_playlist @populate_tracklist
def test_move_group_outside_of_playlist(self): def test_move_group_outside_of_playlist(self):
tracks = len(self.controller.tracks) tracks = len(self.controller.tracks)
test = lambda: self.controller.move(0, 2, tracks + 5) test = lambda: self.controller.move(0, 2, tracks + 5)
self.assertRaises(AssertionError, test) self.assertRaises(AssertionError, test)
@populate_playlist @populate_tracklist
def test_move_group_out_of_range(self): def test_move_group_out_of_range(self):
tracks = len(self.controller.tracks) tracks = len(self.controller.tracks)
test = lambda: self.controller.move(tracks + 2, tracks + 3, 0) test = lambda: self.controller.move(tracks + 2, tracks + 3, 0)
self.assertRaises(AssertionError, test) self.assertRaises(AssertionError, test)
@populate_playlist @populate_tracklist
def test_move_group_invalid_group(self): def test_move_group_invalid_group(self):
test = lambda: self.controller.move(2, 1, 0) test = lambda: self.controller.move(2, 1, 0)
self.assertRaises(AssertionError, test) self.assertRaises(AssertionError, test)
@ -209,7 +211,7 @@ class TracklistControllerTest(object):
tracks2 = self.controller.tracks tracks2 = self.controller.tracks
self.assertNotEqual(id(tracks1), id(tracks2)) self.assertNotEqual(id(tracks1), id(tracks2))
@populate_playlist @populate_tracklist
def test_remove(self): def test_remove(self):
track1 = self.controller.tracks[1] track1 = self.controller.tracks[1]
track2 = self.controller.tracks[2] track2 = self.controller.tracks[2]
@ -219,14 +221,14 @@ class TracklistControllerTest(object):
self.assertNotIn(track1, self.controller.tracks) self.assertNotIn(track1, self.controller.tracks)
self.assertEqual(track2, self.controller.tracks[1]) self.assertEqual(track2, self.controller.tracks[1])
@populate_playlist @populate_tracklist
def test_removing_track_that_does_not_exist_does_nothing(self): def test_removing_track_that_does_not_exist_does_nothing(self):
self.controller.remove(uri='/nonexistant') self.controller.remove(uri='/nonexistant')
def test_removing_from_empty_playlist_does_nothing(self): def test_removing_from_empty_playlist_does_nothing(self):
self.controller.remove(uri='/nonexistant') self.controller.remove(uri='/nonexistant')
@populate_playlist @populate_tracklist
def test_shuffle(self): def test_shuffle(self):
random.seed(1) random.seed(1)
self.controller.shuffle() self.controller.shuffle()
@ -236,7 +238,7 @@ class TracklistControllerTest(object):
self.assertNotEqual(self.tracks, shuffled_tracks) self.assertNotEqual(self.tracks, shuffled_tracks)
self.assertEqual(set(self.tracks), set(shuffled_tracks)) self.assertEqual(set(self.tracks), set(shuffled_tracks))
@populate_playlist @populate_tracklist
def test_shuffle_subset(self): def test_shuffle_subset(self):
random.seed(1) random.seed(1)
self.controller.shuffle(1, 3) self.controller.shuffle(1, 3)
@ -247,18 +249,18 @@ class TracklistControllerTest(object):
self.assertEqual(self.tracks[0], shuffled_tracks[0]) self.assertEqual(self.tracks[0], shuffled_tracks[0])
self.assertEqual(set(self.tracks), set(shuffled_tracks)) self.assertEqual(set(self.tracks), set(shuffled_tracks))
@populate_playlist @populate_tracklist
def test_shuffle_invalid_subset(self): def test_shuffle_invalid_subset(self):
test = lambda: self.controller.shuffle(3, 1) test = lambda: self.controller.shuffle(3, 1)
self.assertRaises(AssertionError, test) self.assertRaises(AssertionError, test)
@populate_playlist @populate_tracklist
def test_shuffle_superset(self): def test_shuffle_superset(self):
tracks = len(self.controller.tracks) tracks = len(self.controller.tracks)
test = lambda: self.controller.shuffle(1, tracks + 5) test = lambda: self.controller.shuffle(1, tracks + 5)
self.assertRaises(AssertionError, test) self.assertRaises(AssertionError, test)
@populate_playlist @populate_tracklist
def test_shuffle_open_subset(self): def test_shuffle_open_subset(self):
random.seed(1) random.seed(1)
self.controller.shuffle(1) self.controller.shuffle(1)
@ -269,24 +271,24 @@ class TracklistControllerTest(object):
self.assertEqual(self.tracks[0], shuffled_tracks[0]) self.assertEqual(self.tracks[0], shuffled_tracks[0])
self.assertEqual(set(self.tracks), set(shuffled_tracks)) self.assertEqual(set(self.tracks), set(shuffled_tracks))
@populate_playlist @populate_tracklist
def test_slice_returns_a_subset_of_tracks(self): def test_slice_returns_a_subset_of_tracks(self):
track_slice = self.controller.slice(1, 3) track_slice = self.controller.slice(1, 3)
self.assertEqual(2, len(track_slice)) self.assertEqual(2, len(track_slice))
self.assertEqual(self.tracks[1], track_slice[0].track) self.assertEqual(self.tracks[1], track_slice[0].track)
self.assertEqual(self.tracks[2], track_slice[1].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): 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(7, 8)))
self.assertEqual(0, len(self.controller.slice(-1, 1))) 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 version = self.controller.version
self.controller.append([]) self.controller.add([])
self.assertEquals(version, self.controller.version) 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 version = self.controller.version
self.controller.append([Track()]) self.controller.add([Track()])
self.assertLess(version, self.controller.version) self.assertLess(version, self.controller.version)

View File

@ -27,7 +27,7 @@ class LocalPlaybackControllerTest(PlaybackControllerTest, unittest.TestCase):
def add_track(self, path): def add_track(self, path):
uri = path_to_uri(path_to_data_dir(path)) uri = path_to_uri(path_to_data_dir(path))
track = Track(uri=uri, length=4464) track = Track(uri=uri, length=4464)
self.tracklist.add(track) self.tracklist.add([track])
def test_uri_scheme(self): def test_uri_scheme(self):
self.assertIn('file', self.core.uri_schemes) self.assertIn('file', self.core.uri_schemes)

View File

@ -26,14 +26,14 @@ class BackendEventsTest(unittest.TestCase):
self.assertEqual(send.call_args[0][0], 'playlists_loaded') self.assertEqual(send.call_args[0][0], 'playlists_loaded')
def test_pause_sends_track_playback_paused_event(self, send): 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() self.core.playback.play().get()
send.reset_mock() send.reset_mock()
self.core.playback.pause().get() self.core.playback.pause().get()
self.assertEqual(send.call_args[0][0], 'track_playback_paused') self.assertEqual(send.call_args[0][0], 'track_playback_paused')
def test_resume_sends_track_playback_resumed(self, send): 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.play()
self.core.playback.pause().get() self.core.playback.pause().get()
send.reset_mock() send.reset_mock()
@ -41,20 +41,20 @@ class BackendEventsTest(unittest.TestCase):
self.assertEqual(send.call_args[0][0], 'track_playback_resumed') self.assertEqual(send.call_args[0][0], 'track_playback_resumed')
def test_play_sends_track_playback_started_event(self, send): 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() send.reset_mock()
self.core.playback.play().get() self.core.playback.play().get()
self.assertEqual(send.call_args[0][0], 'track_playback_started') self.assertEqual(send.call_args[0][0], 'track_playback_started')
def test_stop_sends_track_playback_ended_event(self, send): 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() self.core.playback.play().get()
send.reset_mock() send.reset_mock()
self.core.playback.stop().get() self.core.playback.stop().get()
self.assertEqual(send.call_args_list[0][0][0], 'track_playback_ended') self.assertEqual(send.call_args_list[0][0][0], 'track_playback_ended')
def test_seek_sends_seeked_event(self, send): 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() self.core.playback.play().get()
send.reset_mock() send.reset_mock()
self.core.playback.seek(1000).get() self.core.playback.seek(1000).get()
@ -62,35 +62,30 @@ class BackendEventsTest(unittest.TestCase):
def test_tracklist_add_sends_tracklist_changed_event(self, send): def test_tracklist_add_sends_tracklist_changed_event(self, send):
send.reset_mock() send.reset_mock()
self.core.tracklist.add(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_append_sends_tracklist_changed_event(self, send):
send.reset_mock()
self.core.tracklist.append([Track(uri='dummy:a')]).get()
self.assertEqual(send.call_args[0][0], 'tracklist_changed') self.assertEqual(send.call_args[0][0], 'tracklist_changed')
def test_tracklist_clear_sends_tracklist_changed_event(self, send): 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() send.reset_mock()
self.core.tracklist.clear().get() self.core.tracklist.clear().get()
self.assertEqual(send.call_args[0][0], 'tracklist_changed') self.assertEqual(send.call_args[0][0], 'tracklist_changed')
def test_tracklist_move_sends_tracklist_changed_event(self, send): 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() [Track(uri='dummy:a'), Track(uri='dummy:b')]).get()
send.reset_mock() send.reset_mock()
self.core.tracklist.move(0, 1, 1).get() self.core.tracklist.move(0, 1, 1).get()
self.assertEqual(send.call_args[0][0], 'tracklist_changed') self.assertEqual(send.call_args[0][0], 'tracklist_changed')
def test_tracklist_remove_sends_tracklist_changed_event(self, send): 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() send.reset_mock()
self.core.tracklist.remove(uri='dummy:a').get() self.core.tracklist.remove(uri='dummy:a').get()
self.assertEqual(send.call_args[0][0], 'tracklist_changed') self.assertEqual(send.call_args[0][0], 'tracklist_changed')
def test_tracklist_shuffle_sends_tracklist_changed_event(self, send): 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() [Track(uri='dummy:a'), Track(uri='dummy:b')]).get()
send.reset_mock() send.reset_mock()
self.core.tracklist.shuffle().get() self.core.tracklist.shuffle().get()

View File

@ -35,7 +35,7 @@ class CorePlaybackTest(unittest.TestCase):
self.core = Core(audio=None, backends=[ self.core = Core(audio=None, backends=[
self.backend1, self.backend2, self.backend3]) 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.tl_tracks = self.core.tracklist.tl_tracks
self.unplayable_tl_track = self.tl_tracks[2] self.unplayable_tl_track = self.tl_tracks[2]

View File

@ -10,7 +10,7 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase):
needle = Track(uri='dummy://foo') needle = Track(uri='dummy://foo')
self.backend.library.dummy_library = [ self.backend.library.dummy_library = [
Track(), Track(), needle, Track()] Track(), Track(), needle, Track()]
self.core.tracklist.append( self.core.tracklist.add(
[Track(), Track(), Track(), Track(), Track()]) [Track(), Track(), Track(), Track(), Track()])
self.assertEqual(len(self.core.tracklist.tracks.get()), 5) self.assertEqual(len(self.core.tracklist.tracks.get()), 5)
@ -33,7 +33,7 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase):
needle = Track(uri='dummy://foo') needle = Track(uri='dummy://foo')
self.backend.library.dummy_library = [ self.backend.library.dummy_library = [
Track(), Track(), needle, Track()] Track(), Track(), needle, Track()]
self.core.tracklist.append( self.core.tracklist.add(
[Track(), Track(), Track(), Track(), Track()]) [Track(), Track(), Track(), Track(), Track()])
self.assertEqual(len(self.core.tracklist.tracks.get()), 5) self.assertEqual(len(self.core.tracklist.tracks.get()), 5)
@ -52,7 +52,7 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase):
needle = Track(uri='dummy://foo') needle = Track(uri='dummy://foo')
self.backend.library.dummy_library = [ self.backend.library.dummy_library = [
Track(), Track(), needle, Track()] Track(), Track(), needle, Track()]
self.core.tracklist.append( self.core.tracklist.add(
[Track(), Track(), Track(), Track(), Track()]) [Track(), Track(), Track(), Track(), Track()])
self.assertEqual(len(self.core.tracklist.tracks.get()), 5) self.assertEqual(len(self.core.tracklist.tracks.get()), 5)
@ -67,7 +67,7 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase):
needle = Track(uri='dummy://foo') needle = Track(uri='dummy://foo')
self.backend.library.dummy_library = [ self.backend.library.dummy_library = [
Track(), Track(), needle, Track()] Track(), Track(), needle, Track()]
self.core.tracklist.append( self.core.tracklist.add(
[Track(), Track(), Track(), Track(), Track()]) [Track(), Track(), Track(), Track(), Track()])
self.assertEqual(len(self.core.tracklist.tracks.get()), 5) 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') self.assertEqualResponse('ACK [50@0] {addid} No such song')
def test_clear(self): def test_clear(self):
self.core.tracklist.append( self.core.tracklist.add(
[Track(), Track(), Track(), Track(), Track()]) [Track(), Track(), Track(), Track(), Track()])
self.assertEqual(len(self.core.tracklist.tracks.get()), 5) self.assertEqual(len(self.core.tracklist.tracks.get()), 5)
@ -89,7 +89,7 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase):
self.assertInResponse('OK') self.assertInResponse('OK')
def test_delete_songpos(self): def test_delete_songpos(self):
self.core.tracklist.append( self.core.tracklist.add(
[Track(), Track(), Track(), Track(), Track()]) [Track(), Track(), Track(), Track(), Track()])
self.assertEqual(len(self.core.tracklist.tracks.get()), 5) self.assertEqual(len(self.core.tracklist.tracks.get()), 5)
@ -99,7 +99,7 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase):
self.assertInResponse('OK') self.assertInResponse('OK')
def test_delete_songpos_out_of_bounds(self): def test_delete_songpos_out_of_bounds(self):
self.core.tracklist.append( self.core.tracklist.add(
[Track(), Track(), Track(), Track(), Track()]) [Track(), Track(), Track(), Track(), Track()])
self.assertEqual(len(self.core.tracklist.tracks.get()), 5) 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') self.assertEqualResponse('ACK [2@0] {delete} Bad song index')
def test_delete_open_range(self): def test_delete_open_range(self):
self.core.tracklist.append( self.core.tracklist.add(
[Track(), Track(), Track(), Track(), Track()]) [Track(), Track(), Track(), Track(), Track()])
self.assertEqual(len(self.core.tracklist.tracks.get()), 5) self.assertEqual(len(self.core.tracklist.tracks.get()), 5)
@ -117,7 +117,7 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase):
self.assertInResponse('OK') self.assertInResponse('OK')
def test_delete_closed_range(self): def test_delete_closed_range(self):
self.core.tracklist.append( self.core.tracklist.add(
[Track(), Track(), Track(), Track(), Track()]) [Track(), Track(), Track(), Track(), Track()])
self.assertEqual(len(self.core.tracklist.tracks.get()), 5) self.assertEqual(len(self.core.tracklist.tracks.get()), 5)
@ -126,7 +126,7 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase):
self.assertInResponse('OK') self.assertInResponse('OK')
def test_delete_range_out_of_bounds(self): def test_delete_range_out_of_bounds(self):
self.core.tracklist.append( self.core.tracklist.add(
[Track(), Track(), Track(), Track(), Track()]) [Track(), Track(), Track(), Track(), Track()])
self.assertEqual(len(self.core.tracklist.tracks.get()), 5) 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') self.assertEqualResponse('ACK [2@0] {delete} Bad song index')
def test_deleteid(self): 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.assertEqual(len(self.core.tracklist.tracks.get()), 2)
self.sendRequest('deleteid "1"') self.sendRequest('deleteid "1"')
@ -143,7 +143,7 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase):
self.assertInResponse('OK') self.assertInResponse('OK')
def test_deleteid_does_not_exist(self): 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.assertEqual(len(self.core.tracklist.tracks.get()), 2)
self.sendRequest('deleteid "12345"') self.sendRequest('deleteid "12345"')
@ -151,7 +151,7 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase):
self.assertEqualResponse('ACK [50@0] {deleteid} No such song') self.assertEqualResponse('ACK [50@0] {deleteid} No such song')
def test_move_songpos(self): def test_move_songpos(self):
self.core.tracklist.append([ self.core.tracklist.add([
Track(name='a'), Track(name='b'), Track(name='c'), Track(name='a'), Track(name='b'), Track(name='c'),
Track(name='d'), Track(name='e'), Track(name='f'), Track(name='d'), Track(name='e'), Track(name='f'),
]) ])
@ -167,7 +167,7 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase):
self.assertInResponse('OK') self.assertInResponse('OK')
def test_move_open_range(self): 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='a'), Track(name='b'), Track(name='c'),
Track(name='d'), Track(name='e'), Track(name='f'), Track(name='d'), Track(name='e'), Track(name='f'),
]) ])
@ -183,7 +183,7 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase):
self.assertInResponse('OK') self.assertInResponse('OK')
def test_move_closed_range(self): 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='a'), Track(name='b'), Track(name='c'),
Track(name='d'), Track(name='e'), Track(name='f'), Track(name='d'), Track(name='e'), Track(name='f'),
]) ])
@ -199,7 +199,7 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase):
self.assertInResponse('OK') self.assertInResponse('OK')
def test_moveid(self): def test_moveid(self):
self.core.tracklist.append([ self.core.tracklist.add([
Track(name='a'), Track(name='b'), Track(name='c'), Track(name='a'), Track(name='b'), Track(name='c'),
Track(name='d'), Track(name='e'), Track(name='f'), Track(name='d'), Track(name='e'), Track(name='f'),
]) ])
@ -237,8 +237,7 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase):
self.assertEqualResponse('OK') self.assertEqualResponse('OK')
def test_playlistfind_by_filename_in_tracklist(self): def test_playlistfind_by_filename_in_tracklist(self):
self.core.tracklist.append([ self.core.tracklist.add([Track(uri='file:///exists')])
Track(uri='file:///exists')])
self.sendRequest('playlistfind filename "file:///exists"') self.sendRequest('playlistfind filename "file:///exists"')
self.assertInResponse('file: file:///exists') self.assertInResponse('file: file:///exists')
@ -247,7 +246,7 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase):
self.assertInResponse('OK') self.assertInResponse('OK')
def test_playlistid_without_songid(self): 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.sendRequest('playlistid')
self.assertInResponse('Title: a') self.assertInResponse('Title: a')
@ -255,7 +254,7 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase):
self.assertInResponse('OK') self.assertInResponse('OK')
def test_playlistid_with_songid(self): 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.sendRequest('playlistid "1"')
self.assertNotInResponse('Title: a') self.assertNotInResponse('Title: a')
@ -265,13 +264,13 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase):
self.assertInResponse('OK') self.assertInResponse('OK')
def test_playlistid_with_not_existing_songid_fails(self): 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.sendRequest('playlistid "25"')
self.assertEqualResponse('ACK [50@0] {playlistid} No such song') self.assertEqualResponse('ACK [50@0] {playlistid} No such song')
def test_playlistinfo_without_songpos_or_range(self): 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='a'), Track(name='b'), Track(name='c'),
Track(name='d'), Track(name='e'), Track(name='f'), Track(name='d'), Track(name='e'), Track(name='f'),
]) ])
@ -294,7 +293,7 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase):
def test_playlistinfo_with_songpos(self): def test_playlistinfo_with_songpos(self):
# Make the track's CPID not match the playlist position # Make the track's CPID not match the playlist position
self.core.tracklist.tlid = 17 self.core.tracklist.tlid = 17
self.core.tracklist.append([ self.core.tracklist.add([
Track(name='a'), Track(name='b'), Track(name='c'), Track(name='a'), Track(name='b'), Track(name='c'),
Track(name='d'), Track(name='e'), Track(name='f'), Track(name='d'), Track(name='e'), Track(name='f'),
]) ])
@ -320,7 +319,7 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase):
self.assertEqual(response1, response2) self.assertEqual(response1, response2)
def test_playlistinfo_with_open_range(self): 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='a'), Track(name='b'), Track(name='c'),
Track(name='d'), Track(name='e'), Track(name='f'), Track(name='d'), Track(name='e'), Track(name='f'),
]) ])
@ -341,7 +340,7 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase):
self.assertInResponse('OK') self.assertInResponse('OK')
def test_playlistinfo_with_closed_range(self): 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='a'), Track(name='b'), Track(name='c'),
Track(name='d'), Track(name='e'), Track(name='f'), Track(name='d'), Track(name='e'), Track(name='f'),
]) ])
@ -372,7 +371,7 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase):
self.assertEqualResponse('ACK [0@0] {} Not implemented') self.assertEqualResponse('ACK [0@0] {} Not implemented')
def test_plchanges_with_lower_version_returns_changes(self): 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')]) [Track(name='a'), Track(name='b'), Track(name='c')])
self.sendRequest('plchanges "0"') self.sendRequest('plchanges "0"')
@ -382,7 +381,7 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase):
self.assertInResponse('OK') self.assertInResponse('OK')
def test_plchanges_with_equal_version_returns_nothing(self): 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')]) [Track(name='a'), Track(name='b'), Track(name='c')])
self.assertEqual(self.core.tracklist.version.get(), 1) self.assertEqual(self.core.tracklist.version.get(), 1)
@ -393,7 +392,7 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase):
self.assertInResponse('OK') self.assertInResponse('OK')
def test_plchanges_with_greater_version_returns_nothing(self): 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')]) [Track(name='a'), Track(name='b'), Track(name='c')])
self.assertEqual(self.core.tracklist.version.get(), 1) self.assertEqual(self.core.tracklist.version.get(), 1)
@ -404,7 +403,7 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase):
self.assertInResponse('OK') self.assertInResponse('OK')
def test_plchanges_with_minus_one_returns_entire_playlist(self): 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')]) [Track(name='a'), Track(name='b'), Track(name='c')])
self.sendRequest('plchanges "-1"') self.sendRequest('plchanges "-1"')
@ -414,7 +413,7 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase):
self.assertInResponse('OK') self.assertInResponse('OK')
def test_plchanges_without_quotes_works(self): def test_plchanges_without_quotes_works(self):
self.core.tracklist.append( self.core.tracklist.add(
[Track(name='a'), Track(name='b'), Track(name='c')]) [Track(name='a'), Track(name='b'), Track(name='c')])
self.sendRequest('plchanges 0') self.sendRequest('plchanges 0')
@ -424,7 +423,7 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase):
self.assertInResponse('OK') self.assertInResponse('OK')
def test_plchangesposid(self): def test_plchangesposid(self):
self.core.tracklist.append([Track(), Track(), Track()]) self.core.tracklist.add([Track(), Track(), Track()])
self.sendRequest('plchangesposid "0"') self.sendRequest('plchangesposid "0"')
tl_tracks = self.core.tracklist.tl_tracks.get() tl_tracks = self.core.tracklist.tl_tracks.get()
@ -437,7 +436,7 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase):
self.assertInResponse('OK') self.assertInResponse('OK')
def test_shuffle_without_range(self): 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='a'), Track(name='b'), Track(name='c'),
Track(name='d'), Track(name='e'), Track(name='f'), Track(name='d'), Track(name='e'), Track(name='f'),
]) ])
@ -448,7 +447,7 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase):
self.assertInResponse('OK') self.assertInResponse('OK')
def test_shuffle_with_open_range(self): 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='a'), Track(name='b'), Track(name='c'),
Track(name='d'), Track(name='e'), Track(name='f'), Track(name='d'), Track(name='e'), Track(name='f'),
]) ])
@ -464,7 +463,7 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase):
self.assertInResponse('OK') self.assertInResponse('OK')
def test_shuffle_with_closed_range(self): 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='a'), Track(name='b'), Track(name='c'),
Track(name='d'), Track(name='e'), Track(name='f'), Track(name='d'), Track(name='e'), Track(name='f'),
]) ])
@ -480,7 +479,7 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase):
self.assertInResponse('OK') self.assertInResponse('OK')
def test_swap(self): def test_swap(self):
self.core.tracklist.append([ self.core.tracklist.add([
Track(name='a'), Track(name='b'), Track(name='c'), Track(name='a'), Track(name='b'), Track(name='c'),
Track(name='d'), Track(name='e'), Track(name='f'), Track(name='d'), Track(name='e'), Track(name='f'),
]) ])
@ -496,7 +495,7 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase):
self.assertInResponse('OK') self.assertInResponse('OK')
def test_swapid(self): def test_swapid(self):
self.core.tracklist.append([ self.core.tracklist.add([
Track(name='a'), Track(name='b'), Track(name='c'), Track(name='a'), Track(name='b'), Track(name='c'),
Track(name='d'), Track(name='e'), Track(name='f'), Track(name='d'), Track(name='e'), Track(name='f'),
]) ])
@ -512,13 +511,13 @@ class CurrentPlaylistHandlerTest(protocol.BaseTestCase):
self.assertInResponse('OK') self.assertInResponse('OK')
def test_swapid_with_first_id_unknown_should_ack(self): 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.sendRequest('swapid "0" "4"')
self.assertEqualResponse( self.assertEqualResponse(
'ACK [50@0] {swapid} No such song') 'ACK [50@0] {swapid} No such song')
def test_swapid_with_second_id_unknown_should_ack(self): 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.sendRequest('swapid "4" "0"')
self.assertEqualResponse( self.assertEqualResponse(
'ACK [50@0] {swapid} No such song') 'ACK [50@0] {swapid} No such song')

View File

@ -1,5 +1,7 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from mopidy.models import Album, Artist, Track
from tests.frontends.mpd import protocol from tests.frontends.mpd import protocol
@ -119,6 +121,10 @@ class MusicDatabaseFindTest(protocol.BaseTestCase):
self.sendRequest('find album "album_what" artist "artist_what"') self.sendRequest('find album "album_what" artist "artist_what"')
self.assertInResponse('OK') self.assertInResponse('OK')
def test_find_without_filter_value(self):
self.sendRequest('find "album" ""')
self.assertInResponse('OK')
class MusicDatabaseListTest(protocol.BaseTestCase): class MusicDatabaseListTest(protocol.BaseTestCase):
def test_list_foo_returns_ack(self): def test_list_foo_returns_ack(self):
@ -173,6 +179,18 @@ class MusicDatabaseListTest(protocol.BaseTestCase):
'list "artist" "artist" "anartist" "album" "analbum"') 'list "artist" "artist" "anartist" "album" "analbum"')
self.assertInResponse('OK') 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 ### Album
def test_list_album_with_quotes(self): def test_list_album_with_quotes(self):
@ -191,6 +209,10 @@ class MusicDatabaseListTest(protocol.BaseTestCase):
self.sendRequest('list "album" "anartist"') self.sendRequest('list "album" "anartist"')
self.assertInResponse('OK') 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): def test_list_album_by_artist(self):
self.sendRequest('list "album" "artist" "anartist"') self.sendRequest('list "album" "artist" "anartist"')
self.assertInResponse('OK') self.assertInResponse('OK')
@ -216,6 +238,18 @@ class MusicDatabaseListTest(protocol.BaseTestCase):
'list "album" "artist" "anartist" "album" "analbum"') 'list "album" "artist" "anartist" "album" "analbum"')
self.assertInResponse('OK') 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 ### Date
def test_list_date_with_quotes(self): def test_list_date_with_quotes(self):
@ -259,6 +293,17 @@ class MusicDatabaseListTest(protocol.BaseTestCase):
self.sendRequest('list "date" "artist" "anartist" "album" "analbum"') self.sendRequest('list "date" "artist" "anartist" "album" "analbum"')
self.assertInResponse('OK') 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 ### Genre
def test_list_genre_with_quotes(self): def test_list_genre_with_quotes(self):
@ -303,6 +348,10 @@ class MusicDatabaseListTest(protocol.BaseTestCase):
'list "genre" "artist" "anartist" "album" "analbum"') 'list "genre" "artist" "anartist" "album" "analbum"')
self.assertInResponse('OK') self.assertInResponse('OK')
def test_list_genre_without_filter_value(self):
self.sendRequest('list "genre" "artist" ""')
self.assertInResponse('OK')
class MusicDatabaseSearchTest(protocol.BaseTestCase): class MusicDatabaseSearchTest(protocol.BaseTestCase):
def test_search_album(self): def test_search_album(self):
@ -313,6 +362,10 @@ class MusicDatabaseSearchTest(protocol.BaseTestCase):
self.sendRequest('search album "analbum"') self.sendRequest('search album "analbum"')
self.assertInResponse('OK') self.assertInResponse('OK')
def test_search_album_without_filter_value(self):
self.sendRequest('search "album" ""')
self.assertInResponse('OK')
def test_search_artist(self): def test_search_artist(self):
self.sendRequest('search "artist" "anartist"') self.sendRequest('search "artist" "anartist"')
self.assertInResponse('OK') self.assertInResponse('OK')
@ -321,6 +374,10 @@ class MusicDatabaseSearchTest(protocol.BaseTestCase):
self.sendRequest('search artist "anartist"') self.sendRequest('search artist "anartist"')
self.assertInResponse('OK') self.assertInResponse('OK')
def test_search_artist_without_filter_value(self):
self.sendRequest('search "artist" ""')
self.assertInResponse('OK')
def test_search_filename(self): def test_search_filename(self):
self.sendRequest('search "filename" "afilename"') self.sendRequest('search "filename" "afilename"')
self.assertInResponse('OK') self.assertInResponse('OK')
@ -329,6 +386,10 @@ class MusicDatabaseSearchTest(protocol.BaseTestCase):
self.sendRequest('search filename "afilename"') self.sendRequest('search filename "afilename"')
self.assertInResponse('OK') self.assertInResponse('OK')
def test_search_filename_without_filter_value(self):
self.sendRequest('search "filename" ""')
self.assertInResponse('OK')
def test_search_file(self): def test_search_file(self):
self.sendRequest('search "file" "afilename"') self.sendRequest('search "file" "afilename"')
self.assertInResponse('OK') self.assertInResponse('OK')
@ -337,6 +398,10 @@ class MusicDatabaseSearchTest(protocol.BaseTestCase):
self.sendRequest('search file "afilename"') self.sendRequest('search file "afilename"')
self.assertInResponse('OK') self.assertInResponse('OK')
def test_search_file_without_filter_value(self):
self.sendRequest('search "file" ""')
self.assertInResponse('OK')
def test_search_title(self): def test_search_title(self):
self.sendRequest('search "title" "atitle"') self.sendRequest('search "title" "atitle"')
self.assertInResponse('OK') self.assertInResponse('OK')
@ -345,6 +410,10 @@ class MusicDatabaseSearchTest(protocol.BaseTestCase):
self.sendRequest('search title "atitle"') self.sendRequest('search title "atitle"')
self.assertInResponse('OK') self.assertInResponse('OK')
def test_search_title_without_filter_value(self):
self.sendRequest('search "title" ""')
self.assertInResponse('OK')
def test_search_any(self): def test_search_any(self):
self.sendRequest('search "any" "anything"') self.sendRequest('search "any" "anything"')
self.assertInResponse('OK') self.assertInResponse('OK')
@ -353,6 +422,10 @@ class MusicDatabaseSearchTest(protocol.BaseTestCase):
self.sendRequest('search any "anything"') self.sendRequest('search any "anything"')
self.assertInResponse('OK') self.assertInResponse('OK')
def test_search_any_without_filter_value(self):
self.sendRequest('search "any" ""')
self.assertInResponse('OK')
def test_search_date(self): def test_search_date(self):
self.sendRequest('search "date" "2002-01-01"') self.sendRequest('search "date" "2002-01-01"')
self.assertInResponse('OK') self.assertInResponse('OK')
@ -365,6 +438,10 @@ class MusicDatabaseSearchTest(protocol.BaseTestCase):
self.sendRequest('search Date "2005"') self.sendRequest('search Date "2005"')
self.assertInResponse('OK') 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): def test_search_else_should_fail(self):
self.sendRequest('search "sometype" "something"') self.sendRequest('search "sometype" "something"')
self.assertEqualResponse('ACK [2@0] {search} incorrect arguments') self.assertEqualResponse('ACK [2@0] {search} incorrect arguments')

View File

@ -168,7 +168,7 @@ class PlaybackControlHandlerTest(protocol.BaseTestCase):
self.assertInResponse('OK') self.assertInResponse('OK')
def test_pause_off(self): 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('play "0"')
self.sendRequest('pause "1"') self.sendRequest('pause "1"')
@ -177,7 +177,7 @@ class PlaybackControlHandlerTest(protocol.BaseTestCase):
self.assertInResponse('OK') self.assertInResponse('OK')
def test_pause_on(self): 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('play "0"')
self.sendRequest('pause "1"') self.sendRequest('pause "1"')
@ -185,7 +185,7 @@ class PlaybackControlHandlerTest(protocol.BaseTestCase):
self.assertInResponse('OK') self.assertInResponse('OK')
def test_pause_toggle(self): 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.sendRequest('play "0"')
self.assertEqual(PLAYING, self.core.playback.state.get()) self.assertEqual(PLAYING, self.core.playback.state.get())
@ -200,28 +200,28 @@ class PlaybackControlHandlerTest(protocol.BaseTestCase):
self.assertInResponse('OK') self.assertInResponse('OK')
def test_play_without_pos(self): 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.sendRequest('play')
self.assertEqual(PLAYING, self.core.playback.state.get()) self.assertEqual(PLAYING, self.core.playback.state.get())
self.assertInResponse('OK') self.assertInResponse('OK')
def test_play_with_pos(self): 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.sendRequest('play "0"')
self.assertEqual(PLAYING, self.core.playback.state.get()) self.assertEqual(PLAYING, self.core.playback.state.get())
self.assertInResponse('OK') self.assertInResponse('OK')
def test_play_with_pos_without_quotes(self): 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.sendRequest('play 0')
self.assertEqual(PLAYING, self.core.playback.state.get()) self.assertEqual(PLAYING, self.core.playback.state.get())
self.assertInResponse('OK') self.assertInResponse('OK')
def test_play_with_pos_out_of_bounds(self): def test_play_with_pos_out_of_bounds(self):
self.core.tracklist.append([]) self.core.tracklist.add([])
self.sendRequest('play "0"') self.sendRequest('play "0"')
self.assertEqual(STOPPED, self.core.playback.state.get()) 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): def test_play_minus_one_plays_first_in_playlist_if_no_current_track(self):
self.assertEqual(self.core.playback.current_track.get(), None) self.assertEqual(self.core.playback.current_track.get(), None)
self.core.tracklist.append([ self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')])
Track(uri='dummy:a'),
Track(uri='dummy:b'),
])
self.sendRequest('play "-1"') self.sendRequest('play "-1"')
self.assertEqual(PLAYING, self.core.playback.state.get()) self.assertEqual(PLAYING, self.core.playback.state.get())
@ -241,10 +238,7 @@ class PlaybackControlHandlerTest(protocol.BaseTestCase):
self.assertInResponse('OK') self.assertInResponse('OK')
def test_play_minus_one_plays_current_track_if_current_track_is_set(self): def test_play_minus_one_plays_current_track_if_current_track_is_set(self):
self.core.tracklist.append([ self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')])
Track(uri='dummy:a'),
Track(uri='dummy:b'),
])
self.assertEqual(self.core.playback.current_track.get(), None) self.assertEqual(self.core.playback.current_track.get(), None)
self.core.playback.play() self.core.playback.play()
self.core.playback.next() self.core.playback.next()
@ -266,8 +260,7 @@ class PlaybackControlHandlerTest(protocol.BaseTestCase):
self.assertInResponse('OK') self.assertInResponse('OK')
def test_play_minus_is_ignored_if_playing(self): def test_play_minus_is_ignored_if_playing(self):
self.core.tracklist.append([ self.core.tracklist.add([Track(uri='dummy:a', length=40000)])
Track(uri='dummy:a', length=40000)])
self.core.playback.seek(30000) self.core.playback.seek(30000)
self.assertGreaterEqual( self.assertGreaterEqual(
self.core.playback.time_position.get(), 30000) self.core.playback.time_position.get(), 30000)
@ -280,8 +273,7 @@ class PlaybackControlHandlerTest(protocol.BaseTestCase):
self.assertInResponse('OK') self.assertInResponse('OK')
def test_play_minus_one_resumes_if_paused(self): def test_play_minus_one_resumes_if_paused(self):
self.core.tracklist.append([ self.core.tracklist.add([Track(uri='dummy:a', length=40000)])
Track(uri='dummy:a', length=40000)])
self.core.playback.seek(30000) self.core.playback.seek(30000)
self.assertGreaterEqual( self.assertGreaterEqual(
self.core.playback.time_position.get(), 30000) self.core.playback.time_position.get(), 30000)
@ -296,14 +288,14 @@ class PlaybackControlHandlerTest(protocol.BaseTestCase):
self.assertInResponse('OK') self.assertInResponse('OK')
def test_playid(self): 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.sendRequest('playid "0"')
self.assertEqual(PLAYING, self.core.playback.state.get()) self.assertEqual(PLAYING, self.core.playback.state.get())
self.assertInResponse('OK') self.assertInResponse('OK')
def test_playid_without_quotes(self): 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.sendRequest('playid 0')
self.assertEqual(PLAYING, self.core.playback.state.get()) 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): def test_playid_minus_1_plays_first_in_playlist_if_no_current_track(self):
self.assertEqual(self.core.playback.current_track.get(), None) self.assertEqual(self.core.playback.current_track.get(), None)
self.core.tracklist.append([ self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')])
Track(uri='dummy:a'),
Track(uri='dummy:b'),
])
self.sendRequest('playid "-1"') self.sendRequest('playid "-1"')
self.assertEqual(PLAYING, self.core.playback.state.get()) self.assertEqual(PLAYING, self.core.playback.state.get())
@ -323,10 +312,7 @@ class PlaybackControlHandlerTest(protocol.BaseTestCase):
self.assertInResponse('OK') self.assertInResponse('OK')
def test_playid_minus_1_plays_current_track_if_current_track_is_set(self): def test_playid_minus_1_plays_current_track_if_current_track_is_set(self):
self.core.tracklist.append([ self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')])
Track(uri='dummy:a'),
Track(uri='dummy:b'),
])
self.assertEqual(self.core.playback.current_track.get(), None) self.assertEqual(self.core.playback.current_track.get(), None)
self.core.playback.play() self.core.playback.play()
self.core.playback.next() self.core.playback.next()
@ -348,7 +334,7 @@ class PlaybackControlHandlerTest(protocol.BaseTestCase):
self.assertInResponse('OK') self.assertInResponse('OK')
def test_playid_minus_is_ignored_if_playing(self): 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.core.playback.seek(30000)
self.assertGreaterEqual( self.assertGreaterEqual(
self.core.playback.time_position.get(), 30000) self.core.playback.time_position.get(), 30000)
@ -361,7 +347,7 @@ class PlaybackControlHandlerTest(protocol.BaseTestCase):
self.assertInResponse('OK') self.assertInResponse('OK')
def test_playid_minus_one_resumes_if_paused(self): 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.core.playback.seek(30000)
self.assertGreaterEqual( self.assertGreaterEqual(
self.core.playback.time_position.get(), 30000) self.core.playback.time_position.get(), 30000)
@ -376,7 +362,7 @@ class PlaybackControlHandlerTest(protocol.BaseTestCase):
self.assertInResponse('OK') self.assertInResponse('OK')
def test_playid_which_does_not_exist(self): 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.sendRequest('playid "12345"')
self.assertInResponse('ACK [50@0] {playid} No such song') self.assertInResponse('ACK [50@0] {playid} No such song')
@ -386,7 +372,7 @@ class PlaybackControlHandlerTest(protocol.BaseTestCase):
self.assertInResponse('OK') self.assertInResponse('OK')
def test_seek(self): 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"')
self.sendRequest('seek "0" "30"') self.sendRequest('seek "0" "30"')
@ -395,7 +381,7 @@ class PlaybackControlHandlerTest(protocol.BaseTestCase):
def test_seek_with_songpos(self): def test_seek_with_songpos(self):
seek_track = Track(uri='dummy:b', length=40000) seek_track = Track(uri='dummy:b', length=40000)
self.core.tracklist.append( self.core.tracklist.add(
[Track(uri='dummy:a', length=40000), seek_track]) [Track(uri='dummy:a', length=40000), seek_track])
self.sendRequest('seek "1" "30"') self.sendRequest('seek "1" "30"')
@ -403,7 +389,7 @@ class PlaybackControlHandlerTest(protocol.BaseTestCase):
self.assertInResponse('OK') self.assertInResponse('OK')
def test_seek_without_quotes(self): 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')
self.sendRequest('seek 0 30') self.sendRequest('seek 0 30')
@ -412,7 +398,7 @@ class PlaybackControlHandlerTest(protocol.BaseTestCase):
self.assertInResponse('OK') self.assertInResponse('OK')
def test_seekid(self): 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.sendRequest('seekid "0" "30"')
self.assertGreaterEqual( self.assertGreaterEqual(
self.core.playback.time_position.get(), 30000) self.core.playback.time_position.get(), 30000)
@ -420,11 +406,11 @@ class PlaybackControlHandlerTest(protocol.BaseTestCase):
def test_seekid_with_tlid(self): def test_seekid_with_tlid(self):
seek_track = Track(uri='dummy:b', length=40000) seek_track = Track(uri='dummy:b', length=40000)
self.core.tracklist.append( self.core.tracklist.add(
[Track(uri='dummy:a', length=40000), seek_track]) [Track(uri='dummy:a', length=40000), seek_track])
self.sendRequest('seekid "1" "30"') 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.assertEqual(seek_track, self.core.playback.current_track.get())
self.assertInResponse('OK') self.assertInResponse('OK')

View File

@ -38,7 +38,7 @@ class ReflectionHandlerTest(protocol.BaseTestCase):
def test_decoders(self): def test_decoders(self):
self.sendRequest('decoders') self.sendRequest('decoders')
self.assertInResponse('ACK [0@0] {} Not implemented') self.assertInResponse('OK')
def test_notcommands_returns_only_kill_and_ok(self): def test_notcommands_returns_only_kill_and_ok(self):
response = self.sendRequest('notcommands') response = self.sendRequest('notcommands')

View File

@ -18,7 +18,7 @@ class IssueGH17RegressionTest(protocol.BaseTestCase):
- Press next until you get to the unplayable track - Press next until you get to the unplayable track
""" """
def test(self): def test(self):
self.core.tracklist.append([ self.core.tracklist.add([
Track(uri='dummy:a'), Track(uri='dummy:a'),
Track(uri='dummy:b'), Track(uri='dummy:b'),
Track(uri='dummy:error'), Track(uri='dummy:error'),
@ -59,7 +59,7 @@ class IssueGH18RegressionTest(protocol.BaseTestCase):
""" """
def test(self): 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:a'), Track(uri='dummy:b'), Track(uri='dummy:c'),
Track(uri='dummy:d'), Track(uri='dummy:e'), Track(uri='dummy:f')]) Track(uri='dummy:d'), Track(uri='dummy:e'), Track(uri='dummy:f')])
random.seed(1) random.seed(1)
@ -95,7 +95,7 @@ class IssueGH22RegressionTest(protocol.BaseTestCase):
""" """
def test(self): 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:a'), Track(uri='dummy:b'), Track(uri='dummy:c'),
Track(uri='dummy:d'), Track(uri='dummy:e'), Track(uri='dummy:f')]) Track(uri='dummy:d'), Track(uri='dummy:e'), Track(uri='dummy:f')])
random.seed(1) random.seed(1)
@ -124,7 +124,7 @@ class IssueGH69RegressionTest(protocol.BaseTestCase):
def test(self): def test(self):
self.core.playlists.create('foo') 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:a'), Track(uri='dummy:b'), Track(uri='dummy:c'),
Track(uri='dummy:d'), Track(uri='dummy:e'), Track(uri='dummy:f')]) Track(uri='dummy:d'), Track(uri='dummy:e'), Track(uri='dummy:f')])

View File

@ -12,7 +12,7 @@ class StatusHandlerTest(protocol.BaseTestCase):
def test_currentsong(self): def test_currentsong(self):
track = Track() track = Track()
self.core.tracklist.append([track]) self.core.tracklist.add([track])
self.core.playback.play() self.core.playback.play()
self.sendRequest('currentsong') self.sendRequest('currentsong')
self.assertInResponse('file: ') self.assertInResponse('file: ')

View File

@ -10,18 +10,18 @@ from tests.frontends.mpd import protocol
class PlaylistsHandlerTest(protocol.BaseTestCase): class PlaylistsHandlerTest(protocol.BaseTestCase):
def test_listplaylist(self): def test_listplaylist(self):
self.backend.playlists.playlists = [ 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.sendRequest('listplaylist "name"')
self.assertInResponse('file: file:///dev/urandom') self.assertInResponse('file: dummy:a')
self.assertInResponse('OK') self.assertInResponse('OK')
def test_listplaylist_without_quotes(self): def test_listplaylist_without_quotes(self):
self.backend.playlists.playlists = [ 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.sendRequest('listplaylist name')
self.assertInResponse('file: file:///dev/urandom') self.assertInResponse('file: dummy:a')
self.assertInResponse('OK') self.assertInResponse('OK')
def test_listplaylist_fails_if_no_playlist_is_found(self): def test_listplaylist_fails_if_no_playlist_is_found(self):
@ -30,20 +30,20 @@ class PlaylistsHandlerTest(protocol.BaseTestCase):
def test_listplaylistinfo(self): def test_listplaylistinfo(self):
self.backend.playlists.playlists = [ 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.sendRequest('listplaylistinfo "name"')
self.assertInResponse('file: file:///dev/urandom') self.assertInResponse('file: dummy:a')
self.assertInResponse('Track: 0') self.assertInResponse('Track: 0')
self.assertNotInResponse('Pos: 0') self.assertNotInResponse('Pos: 0')
self.assertInResponse('OK') self.assertInResponse('OK')
def test_listplaylistinfo_without_quotes(self): def test_listplaylistinfo_without_quotes(self):
self.backend.playlists.playlists = [ 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.sendRequest('listplaylistinfo name')
self.assertInResponse('file: file:///dev/urandom') self.assertInResponse('file: dummy:a')
self.assertInResponse('Track: 0') self.assertInResponse('Track: 0')
self.assertNotInResponse('Pos: 0') self.assertNotInResponse('Pos: 0')
self.assertInResponse('OK') self.assertInResponse('OK')
@ -64,8 +64,17 @@ class PlaylistsHandlerTest(protocol.BaseTestCase):
self.assertInResponse('Last-Modified: 2001-03-17T13:41:17Z') self.assertInResponse('Last-Modified: 2001-03-17T13:41:17Z')
self.assertInResponse('OK') 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): 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.assertEqual(len(self.core.tracklist.tracks.get()), 2)
self.backend.playlists.playlists = [ self.backend.playlists.playlists = [
Playlist(name='A-list', tracks=[ Playlist(name='A-list', tracks=[
@ -87,7 +96,7 @@ class PlaylistsHandlerTest(protocol.BaseTestCase):
self.assertEqualResponse('ACK [50@0] {load} No such playlist') self.assertEqualResponse('ACK [50@0] {load} No such playlist')
def test_playlistadd(self): def test_playlistadd(self):
self.sendRequest('playlistadd "name" "file:///dev/urandom"') self.sendRequest('playlistadd "name" "dummy:a"')
self.assertEqualResponse('ACK [0@0] {} Not implemented') self.assertEqualResponse('ACK [0@0] {} Not implemented')
def test_playlistclear(self): def test_playlistclear(self):

View File

@ -131,21 +131,21 @@ class StatusHandlerTest(unittest.TestCase):
self.assertEqual(result['state'], 'pause') self.assertEqual(result['state'], 'pause')
def test_status_method_when_playlist_loaded_contains_song(self): 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() self.core.playback.play()
result = dict(status.status(self.context)) result = dict(status.status(self.context))
self.assertIn('song', result) self.assertIn('song', result)
self.assertGreaterEqual(int(result['song']), 0) self.assertGreaterEqual(int(result['song']), 0)
def test_status_method_when_playlist_loaded_contains_tlid_as_songid(self): 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() self.core.playback.play()
result = dict(status.status(self.context)) result = dict(status.status(self.context))
self.assertIn('songid', result) self.assertIn('songid', result)
self.assertEqual(int(result['songid']), 0) self.assertEqual(int(result['songid']), 0)
def test_status_method_when_playing_contains_time_with_no_length(self): 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() self.core.playback.play()
result = dict(status.status(self.context)) result = dict(status.status(self.context))
self.assertIn('time', result) self.assertIn('time', result)
@ -155,7 +155,7 @@ class StatusHandlerTest(unittest.TestCase):
self.assertLessEqual(position, total) self.assertLessEqual(position, total)
def test_status_method_when_playing_contains_time_with_length(self): 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() self.core.playback.play()
result = dict(status.status(self.context)) result = dict(status.status(self.context))
self.assertIn('time', result) self.assertIn('time', result)
@ -165,7 +165,7 @@ class StatusHandlerTest(unittest.TestCase):
self.assertLessEqual(position, total) self.assertLessEqual(position, total)
def test_status_method_when_playing_contains_elapsed(self): 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.play()
self.core.playback.pause() self.core.playback.pause()
self.core.playback.seek(59123) self.core.playback.seek(59123)
@ -174,7 +174,7 @@ class StatusHandlerTest(unittest.TestCase):
self.assertEqual(result['elapsed'], '59.123') self.assertEqual(result['elapsed'], '59.123')
def test_status_method_when_starting_playing_contains_elapsed_zero(self): 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.play()
self.core.playback.pause() self.core.playback.pause()
result = dict(status.status(self.context)) result = dict(status.status(self.context))
@ -182,7 +182,7 @@ class StatusHandlerTest(unittest.TestCase):
self.assertEqual(result['elapsed'], '0.000') self.assertEqual(result['elapsed'], '0.000')
def test_status_method_when_playing_contains_bitrate(self): 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() self.core.playback.play()
result = dict(status.status(self.context)) result = dict(status.status(self.context))
self.assertIn('bitrate', result) self.assertIn('bitrate', result)

View File

@ -101,16 +101,14 @@ class PlayerInterfaceTest(unittest.TestCase):
def test_set_rate_is_ignored_if_can_control_is_false(self): def test_set_rate_is_ignored_if_can_control_is_false(self):
self.mpris.get_CanControl = lambda *_: False self.mpris.get_CanControl = lambda *_: False
self.core.tracklist.append([ self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')])
Track(uri='dummy:a'), Track(uri='dummy:b')])
self.core.playback.play() self.core.playback.play()
self.assertEqual(self.core.playback.state.get(), PLAYING) self.assertEqual(self.core.playback.state.get(), PLAYING)
self.mpris.Set(objects.PLAYER_IFACE, 'Rate', 0) self.mpris.Set(objects.PLAYER_IFACE, 'Rate', 0)
self.assertEqual(self.core.playback.state.get(), PLAYING) self.assertEqual(self.core.playback.state.get(), PLAYING)
def test_set_rate_to_zero_pauses_playback(self): def test_set_rate_to_zero_pauses_playback(self):
self.core.tracklist.append([ self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')])
Track(uri='dummy:a'), Track(uri='dummy:b')])
self.core.playback.play() self.core.playback.play()
self.assertEqual(self.core.playback.state.get(), PLAYING) self.assertEqual(self.core.playback.state.get(), PLAYING)
self.mpris.Set(objects.PLAYER_IFACE, 'Rate', 0) self.mpris.Set(objects.PLAYER_IFACE, 'Rate', 0)
@ -150,7 +148,7 @@ class PlayerInterfaceTest(unittest.TestCase):
self.assertEqual(result['mpris:trackid'], '') self.assertEqual(result['mpris:trackid'], '')
def test_get_metadata_has_trackid_based_on_tlid(self): 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() self.core.playback.play()
(tlid, track) = self.core.playback.current_tl_track.get() (tlid, track) = self.core.playback.current_tl_track.get()
result = self.mpris.Get(objects.PLAYER_IFACE, 'Metadata') result = self.mpris.Get(objects.PLAYER_IFACE, 'Metadata')
@ -159,28 +157,28 @@ class PlayerInterfaceTest(unittest.TestCase):
result['mpris:trackid'], '/com/mopidy/track/%d' % tlid) result['mpris:trackid'], '/com/mopidy/track/%d' % tlid)
def test_get_metadata_has_track_length(self): 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() self.core.playback.play()
result = self.mpris.Get(objects.PLAYER_IFACE, 'Metadata') result = self.mpris.Get(objects.PLAYER_IFACE, 'Metadata')
self.assertIn('mpris:length', result.keys()) self.assertIn('mpris:length', result.keys())
self.assertEqual(result['mpris:length'], 40000000) self.assertEqual(result['mpris:length'], 40000000)
def test_get_metadata_has_track_uri(self): 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() self.core.playback.play()
result = self.mpris.Get(objects.PLAYER_IFACE, 'Metadata') result = self.mpris.Get(objects.PLAYER_IFACE, 'Metadata')
self.assertIn('xesam:url', result.keys()) self.assertIn('xesam:url', result.keys())
self.assertEqual(result['xesam:url'], 'dummy:a') self.assertEqual(result['xesam:url'], 'dummy:a')
def test_get_metadata_has_track_title(self): 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() self.core.playback.play()
result = self.mpris.Get(objects.PLAYER_IFACE, 'Metadata') result = self.mpris.Get(objects.PLAYER_IFACE, 'Metadata')
self.assertIn('xesam:title', result.keys()) self.assertIn('xesam:title', result.keys())
self.assertEqual(result['xesam:title'], 'a') self.assertEqual(result['xesam:title'], 'a')
def test_get_metadata_has_track_artists(self): 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)])]) Artist(name='a'), Artist(name='b'), Artist(name=None)])])
self.core.playback.play() self.core.playback.play()
result = self.mpris.Get(objects.PLAYER_IFACE, 'Metadata') result = self.mpris.Get(objects.PLAYER_IFACE, 'Metadata')
@ -188,14 +186,14 @@ class PlayerInterfaceTest(unittest.TestCase):
self.assertEqual(result['xesam:artist'], ['a', 'b']) self.assertEqual(result['xesam:artist'], ['a', 'b'])
def test_get_metadata_has_track_album(self): 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() self.core.playback.play()
result = self.mpris.Get(objects.PLAYER_IFACE, 'Metadata') result = self.mpris.Get(objects.PLAYER_IFACE, 'Metadata')
self.assertIn('xesam:album', result.keys()) self.assertIn('xesam:album', result.keys())
self.assertEqual(result['xesam:album'], 'a') self.assertEqual(result['xesam:album'], 'a')
def test_get_metadata_has_track_album_artists(self): 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)]))]) Artist(name='a'), Artist(name='b'), Artist(name=None)]))])
self.core.playback.play() self.core.playback.play()
result = self.mpris.Get(objects.PLAYER_IFACE, 'Metadata') result = self.mpris.Get(objects.PLAYER_IFACE, 'Metadata')
@ -203,7 +201,7 @@ class PlayerInterfaceTest(unittest.TestCase):
self.assertEqual(result['xesam:albumArtist'], ['a', 'b']) self.assertEqual(result['xesam:albumArtist'], ['a', 'b'])
def test_get_metadata_has_track_number_in_album(self): 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() self.core.playback.play()
result = self.mpris.Get(objects.PLAYER_IFACE, 'Metadata') result = self.mpris.Get(objects.PLAYER_IFACE, 'Metadata')
self.assertIn('xesam:trackNumber', result.keys()) self.assertIn('xesam:trackNumber', result.keys())
@ -246,7 +244,7 @@ class PlayerInterfaceTest(unittest.TestCase):
self.assertEqual(self.core.playback.volume.get(), 10) self.assertEqual(self.core.playback.volume.get(), 10)
def test_get_position_returns_time_position_in_microseconds(self): 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.play()
self.core.playback.seek(10000) self.core.playback.seek(10000)
result_in_microseconds = self.mpris.Get( 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): def test_can_go_next_is_true_if_can_control_and_other_next_track(self):
self.mpris.get_CanControl = lambda *_: True self.mpris.get_CanControl = lambda *_: True
self.core.tracklist.append([ self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')])
Track(uri='dummy:a'), Track(uri='dummy:b')])
self.core.playback.play() self.core.playback.play()
result = self.mpris.Get(objects.PLAYER_IFACE, 'CanGoNext') result = self.mpris.Get(objects.PLAYER_IFACE, 'CanGoNext')
self.assertTrue(result) self.assertTrue(result)
def test_can_go_next_is_false_if_next_track_is_the_same(self): def test_can_go_next_is_false_if_next_track_is_the_same(self):
self.mpris.get_CanControl = lambda *_: True 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.repeat = True
self.core.playback.play() self.core.playback.play()
result = self.mpris.Get(objects.PLAYER_IFACE, 'CanGoNext') 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): def test_can_go_next_is_false_if_can_control_is_false(self):
self.mpris.get_CanControl = lambda *_: False self.mpris.get_CanControl = lambda *_: False
self.core.tracklist.append([ self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')])
Track(uri='dummy:a'), Track(uri='dummy:b')])
self.core.playback.play() self.core.playback.play()
result = self.mpris.Get(objects.PLAYER_IFACE, 'CanGoNext') result = self.mpris.Get(objects.PLAYER_IFACE, 'CanGoNext')
self.assertFalse(result) self.assertFalse(result)
def test_can_go_previous_is_true_if_can_control_and_previous_track(self): def test_can_go_previous_is_true_if_can_control_and_previous_track(self):
self.mpris.get_CanControl = lambda *_: True self.mpris.get_CanControl = lambda *_: True
self.core.tracklist.append([ self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')])
Track(uri='dummy:a'), Track(uri='dummy:b')])
self.core.playback.play() self.core.playback.play()
self.core.playback.next() self.core.playback.next()
result = self.mpris.Get(objects.PLAYER_IFACE, 'CanGoPrevious') 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): def test_can_go_previous_is_false_if_previous_track_is_the_same(self):
self.mpris.get_CanControl = lambda *_: True 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.repeat = True
self.core.playback.play() self.core.playback.play()
result = self.mpris.Get(objects.PLAYER_IFACE, 'CanGoPrevious') 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): def test_can_go_previous_is_false_if_can_control_is_false(self):
self.mpris.get_CanControl = lambda *_: False self.mpris.get_CanControl = lambda *_: False
self.core.tracklist.append([ self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')])
Track(uri='dummy:a'), Track(uri='dummy:b')])
self.core.playback.play() self.core.playback.play()
self.core.playback.next() self.core.playback.next()
result = self.mpris.Get(objects.PLAYER_IFACE, 'CanGoPrevious') 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): def test_can_play_is_true_if_can_control_and_current_track(self):
self.mpris.get_CanControl = lambda *_: True 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.core.playback.play()
self.assertTrue(self.core.playback.current_track.get()) self.assertTrue(self.core.playback.current_track.get())
result = self.mpris.Get(objects.PLAYER_IFACE, 'CanPlay') 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): def test_next_is_ignored_if_can_go_next_is_false(self):
self.mpris.get_CanGoNext = lambda *_: False self.mpris.get_CanGoNext = lambda *_: False
self.core.tracklist.append([ self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')])
Track(uri='dummy:a'), Track(uri='dummy:b')])
self.core.playback.play() self.core.playback.play()
self.assertEqual(self.core.playback.current_track.get().uri, 'dummy:a') self.assertEqual(self.core.playback.current_track.get().uri, 'dummy:a')
self.mpris.Next() self.mpris.Next()
self.assertEqual(self.core.playback.current_track.get().uri, 'dummy:a') self.assertEqual(self.core.playback.current_track.get().uri, 'dummy:a')
def test_next_when_playing_skips_to_next_track_and_keep_playing(self): def test_next_when_playing_skips_to_next_track_and_keep_playing(self):
self.core.tracklist.append([ self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')])
Track(uri='dummy:a'), Track(uri='dummy:b')])
self.core.playback.play() self.core.playback.play()
self.assertEqual(self.core.playback.current_track.get().uri, 'dummy:a') self.assertEqual(self.core.playback.current_track.get().uri, 'dummy:a')
self.assertEqual(self.core.playback.state.get(), PLAYING) self.assertEqual(self.core.playback.state.get(), PLAYING)
@ -381,8 +373,7 @@ class PlayerInterfaceTest(unittest.TestCase):
self.assertEqual(self.core.playback.state.get(), PLAYING) self.assertEqual(self.core.playback.state.get(), PLAYING)
def test_next_when_at_end_of_list_should_stop_playback(self): def test_next_when_at_end_of_list_should_stop_playback(self):
self.core.tracklist.append([ self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')])
Track(uri='dummy:a'), Track(uri='dummy:b')])
self.core.playback.play() self.core.playback.play()
self.core.playback.next() self.core.playback.next()
self.assertEqual(self.core.playback.current_track.get().uri, 'dummy:b') 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) self.assertEqual(self.core.playback.state.get(), STOPPED)
def test_next_when_paused_should_skip_to_next_track_and_stay_paused(self): def test_next_when_paused_should_skip_to_next_track_and_stay_paused(self):
self.core.tracklist.append([ self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')])
Track(uri='dummy:a'), Track(uri='dummy:b')])
self.core.playback.play() self.core.playback.play()
self.core.playback.pause() self.core.playback.pause()
self.assertEqual(self.core.playback.current_track.get().uri, 'dummy:a') 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) self.assertEqual(self.core.playback.state.get(), PAUSED)
def test_next_when_stopped_skips_to_next_track_and_stay_stopped(self): def test_next_when_stopped_skips_to_next_track_and_stay_stopped(self):
self.core.tracklist.append([ self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')])
Track(uri='dummy:a'), Track(uri='dummy:b')])
self.core.playback.play() self.core.playback.play()
self.core.playback.stop() self.core.playback.stop()
self.assertEqual(self.core.playback.current_track.get().uri, 'dummy:a') 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): def test_previous_is_ignored_if_can_go_previous_is_false(self):
self.mpris.get_CanGoPrevious = lambda *_: False self.mpris.get_CanGoPrevious = lambda *_: False
self.core.tracklist.append([ self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')])
Track(uri='dummy:a'), Track(uri='dummy:b')])
self.core.playback.play() self.core.playback.play()
self.core.playback.next() self.core.playback.next()
self.assertEqual(self.core.playback.current_track.get().uri, 'dummy:b') 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') self.assertEqual(self.core.playback.current_track.get().uri, 'dummy:b')
def test_previous_when_playing_skips_to_prev_track_and_keep_playing(self): def test_previous_when_playing_skips_to_prev_track_and_keep_playing(self):
self.core.tracklist.append([ self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')])
Track(uri='dummy:a'), Track(uri='dummy:b')])
self.core.playback.play() self.core.playback.play()
self.core.playback.next() self.core.playback.next()
self.assertEqual(self.core.playback.current_track.get().uri, 'dummy:b') 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) self.assertEqual(self.core.playback.state.get(), PLAYING)
def test_previous_when_at_start_of_list_should_stop_playback(self): def test_previous_when_at_start_of_list_should_stop_playback(self):
self.core.tracklist.append([ self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')])
Track(uri='dummy:a'), Track(uri='dummy:b')])
self.core.playback.play() self.core.playback.play()
self.assertEqual(self.core.playback.current_track.get().uri, 'dummy:a') self.assertEqual(self.core.playback.current_track.get().uri, 'dummy:a')
self.assertEqual(self.core.playback.state.get(), PLAYING) self.assertEqual(self.core.playback.state.get(), PLAYING)
@ -443,8 +429,7 @@ class PlayerInterfaceTest(unittest.TestCase):
self.assertEqual(self.core.playback.state.get(), STOPPED) self.assertEqual(self.core.playback.state.get(), STOPPED)
def test_previous_when_paused_skips_to_previous_track_and_pause(self): def test_previous_when_paused_skips_to_previous_track_and_pause(self):
self.core.tracklist.append([ self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')])
Track(uri='dummy:a'), Track(uri='dummy:b')])
self.core.playback.play() self.core.playback.play()
self.core.playback.next() self.core.playback.next()
self.core.playback.pause() self.core.playback.pause()
@ -455,8 +440,7 @@ class PlayerInterfaceTest(unittest.TestCase):
self.assertEqual(self.core.playback.state.get(), PAUSED) self.assertEqual(self.core.playback.state.get(), PAUSED)
def test_previous_when_stopped_skips_to_previous_track_and_stops(self): def test_previous_when_stopped_skips_to_previous_track_and_stops(self):
self.core.tracklist.append([ self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')])
Track(uri='dummy:a'), Track(uri='dummy:b')])
self.core.playback.play() self.core.playback.play()
self.core.playback.next() self.core.playback.next()
self.core.playback.stop() self.core.playback.stop()
@ -468,24 +452,21 @@ class PlayerInterfaceTest(unittest.TestCase):
def test_pause_is_ignored_if_can_pause_is_false(self): def test_pause_is_ignored_if_can_pause_is_false(self):
self.mpris.get_CanPause = lambda *_: False self.mpris.get_CanPause = lambda *_: False
self.core.tracklist.append([ self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')])
Track(uri='dummy:a'), Track(uri='dummy:b')])
self.core.playback.play() self.core.playback.play()
self.assertEqual(self.core.playback.state.get(), PLAYING) self.assertEqual(self.core.playback.state.get(), PLAYING)
self.mpris.Pause() self.mpris.Pause()
self.assertEqual(self.core.playback.state.get(), PLAYING) self.assertEqual(self.core.playback.state.get(), PLAYING)
def test_pause_when_playing_should_pause_playback(self): def test_pause_when_playing_should_pause_playback(self):
self.core.tracklist.append([ self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')])
Track(uri='dummy:a'), Track(uri='dummy:b')])
self.core.playback.play() self.core.playback.play()
self.assertEqual(self.core.playback.state.get(), PLAYING) self.assertEqual(self.core.playback.state.get(), PLAYING)
self.mpris.Pause() self.mpris.Pause()
self.assertEqual(self.core.playback.state.get(), PAUSED) self.assertEqual(self.core.playback.state.get(), PAUSED)
def test_pause_when_paused_has_no_effect(self): def test_pause_when_paused_has_no_effect(self):
self.core.tracklist.append([ self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')])
Track(uri='dummy:a'), Track(uri='dummy:b')])
self.core.playback.play() self.core.playback.play()
self.core.playback.pause() self.core.playback.pause()
self.assertEqual(self.core.playback.state.get(), PAUSED) 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): def test_playpause_is_ignored_if_can_pause_is_false(self):
self.mpris.get_CanPause = lambda *_: False self.mpris.get_CanPause = lambda *_: False
self.core.tracklist.append([ self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')])
Track(uri='dummy:a'), Track(uri='dummy:b')])
self.core.playback.play() self.core.playback.play()
self.assertEqual(self.core.playback.state.get(), PLAYING) self.assertEqual(self.core.playback.state.get(), PLAYING)
self.mpris.PlayPause() self.mpris.PlayPause()
self.assertEqual(self.core.playback.state.get(), PLAYING) self.assertEqual(self.core.playback.state.get(), PLAYING)
def test_playpause_when_playing_should_pause_playback(self): def test_playpause_when_playing_should_pause_playback(self):
self.core.tracklist.append([ self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')])
Track(uri='dummy:a'), Track(uri='dummy:b')])
self.core.playback.play() self.core.playback.play()
self.assertEqual(self.core.playback.state.get(), PLAYING) self.assertEqual(self.core.playback.state.get(), PLAYING)
self.mpris.PlayPause() self.mpris.PlayPause()
self.assertEqual(self.core.playback.state.get(), PAUSED) self.assertEqual(self.core.playback.state.get(), PAUSED)
def test_playpause_when_paused_should_resume_playback(self): def test_playpause_when_paused_should_resume_playback(self):
self.core.tracklist.append([ self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')])
Track(uri='dummy:a'), Track(uri='dummy:b')])
self.core.playback.play() self.core.playback.play()
self.core.playback.pause() self.core.playback.pause()
@ -526,32 +504,28 @@ class PlayerInterfaceTest(unittest.TestCase):
self.assertGreaterEqual(after_pause, at_pause) self.assertGreaterEqual(after_pause, at_pause)
def test_playpause_when_stopped_should_start_playback(self): def test_playpause_when_stopped_should_start_playback(self):
self.core.tracklist.append([ self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')])
Track(uri='dummy:a'), Track(uri='dummy:b')])
self.assertEqual(self.core.playback.state.get(), STOPPED) self.assertEqual(self.core.playback.state.get(), STOPPED)
self.mpris.PlayPause() self.mpris.PlayPause()
self.assertEqual(self.core.playback.state.get(), PLAYING) self.assertEqual(self.core.playback.state.get(), PLAYING)
def test_stop_is_ignored_if_can_control_is_false(self): def test_stop_is_ignored_if_can_control_is_false(self):
self.mpris.get_CanControl = lambda *_: False self.mpris.get_CanControl = lambda *_: False
self.core.tracklist.append([ self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')])
Track(uri='dummy:a'), Track(uri='dummy:b')])
self.core.playback.play() self.core.playback.play()
self.assertEqual(self.core.playback.state.get(), PLAYING) self.assertEqual(self.core.playback.state.get(), PLAYING)
self.mpris.Stop() self.mpris.Stop()
self.assertEqual(self.core.playback.state.get(), PLAYING) self.assertEqual(self.core.playback.state.get(), PLAYING)
def test_stop_when_playing_should_stop_playback(self): def test_stop_when_playing_should_stop_playback(self):
self.core.tracklist.append([ self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')])
Track(uri='dummy:a'), Track(uri='dummy:b')])
self.core.playback.play() self.core.playback.play()
self.assertEqual(self.core.playback.state.get(), PLAYING) self.assertEqual(self.core.playback.state.get(), PLAYING)
self.mpris.Stop() self.mpris.Stop()
self.assertEqual(self.core.playback.state.get(), STOPPED) self.assertEqual(self.core.playback.state.get(), STOPPED)
def test_stop_when_paused_should_stop_playback(self): def test_stop_when_paused_should_stop_playback(self):
self.core.tracklist.append([ self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')])
Track(uri='dummy:a'), Track(uri='dummy:b')])
self.core.playback.play() self.core.playback.play()
self.core.playback.pause() self.core.playback.pause()
self.assertEqual(self.core.playback.state.get(), PAUSED) 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): def test_play_is_ignored_if_can_play_is_false(self):
self.mpris.get_CanPlay = lambda *_: False self.mpris.get_CanPlay = lambda *_: False
self.core.tracklist.append([ self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')])
Track(uri='dummy:a'), Track(uri='dummy:b')])
self.assertEqual(self.core.playback.state.get(), STOPPED) self.assertEqual(self.core.playback.state.get(), STOPPED)
self.mpris.Play() self.mpris.Play()
self.assertEqual(self.core.playback.state.get(), STOPPED) self.assertEqual(self.core.playback.state.get(), STOPPED)
def test_play_when_stopped_starts_playback(self): def test_play_when_stopped_starts_playback(self):
self.core.tracklist.append([ self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')])
Track(uri='dummy:a'), Track(uri='dummy:b')])
self.assertEqual(self.core.playback.state.get(), STOPPED) self.assertEqual(self.core.playback.state.get(), STOPPED)
self.mpris.Play() self.mpris.Play()
self.assertEqual(self.core.playback.state.get(), PLAYING) self.assertEqual(self.core.playback.state.get(), PLAYING)
def test_play_after_pause_resumes_from_same_position(self): 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() self.core.playback.play()
before_pause = self.core.playback.time_position.get() 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): def test_seek_is_ignored_if_can_seek_is_false(self):
self.mpris.get_CanSeek = lambda *_: False 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() self.core.playback.play()
before_seek = self.core.playback.time_position.get() 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) self.assertLess(after_seek, before_seek + milliseconds_to_seek)
def test_seek_seeks_given_microseconds_forward_in_the_current_track(self): 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() self.core.playback.play()
before_seek = self.core.playback.time_position.get() 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) self.assertGreaterEqual(after_seek, before_seek + milliseconds_to_seek)
def test_seek_seeks_given_microseconds_backward_if_negative(self): 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.play()
self.core.playback.seek(20000) self.core.playback.seek(20000)
@ -650,7 +622,7 @@ class PlayerInterfaceTest(unittest.TestCase):
self.assertLess(after_seek, before_seek) self.assertLess(after_seek, before_seek)
def test_seek_seeks_to_start_of_track_if_new_position_is_negative(self): 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.play()
self.core.playback.seek(20000) self.core.playback.seek(20000)
@ -670,7 +642,7 @@ class PlayerInterfaceTest(unittest.TestCase):
self.assertGreaterEqual(after_seek, 0) self.assertGreaterEqual(after_seek, 0)
def test_seek_skips_to_next_track_if_new_position_gt_track_length(self): 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:a', length=40000),
Track(uri='dummy:b')]) Track(uri='dummy:b')])
self.core.playback.play() self.core.playback.play()
@ -695,7 +667,7 @@ class PlayerInterfaceTest(unittest.TestCase):
def test_set_position_is_ignored_if_can_seek_is_false(self): def test_set_position_is_ignored_if_can_seek_is_false(self):
self.mpris.get_CanSeek = lambda *_: False 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() self.core.playback.play()
before_set_position = self.core.playback.time_position.get() 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) self.assertLess(after_set_position, position_to_set_in_millisec)
def test_set_position_sets_the_current_track_position_in_microsecs(self): 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() self.core.playback.play()
before_set_position = self.core.playback.time_position.get() 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) after_set_position, position_to_set_in_millisec)
def test_set_position_does_nothing_if_the_position_is_negative(self): 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.play()
self.core.playback.seek(20000) self.core.playback.seek(20000)
@ -757,7 +729,7 @@ class PlayerInterfaceTest(unittest.TestCase):
self.assertEqual(self.core.playback.current_track.get().uri, 'dummy:a') self.assertEqual(self.core.playback.current_track.get().uri, 'dummy:a')
def test_set_position_does_nothing_if_position_is_gt_track_length(self): 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.play()
self.core.playback.seek(20000) self.core.playback.seek(20000)
@ -780,7 +752,7 @@ class PlayerInterfaceTest(unittest.TestCase):
self.assertEqual(self.core.playback.current_track.get().uri, 'dummy:a') self.assertEqual(self.core.playback.current_track.get().uri, 'dummy:a')
def test_set_position_is_noop_if_track_id_isnt_current_track(self): 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.play()
self.core.playback.seek(20000) 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): def test_open_uri_starts_playback_of_new_track_if_stopped(self):
self.mpris.get_CanPlay = lambda *_: True self.mpris.get_CanPlay = lambda *_: True
self.backend.library.dummy_library = [Track(uri='dummy:/test/uri')] self.backend.library.dummy_library = [Track(uri='dummy:/test/uri')]
self.core.tracklist.append([ self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')])
Track(uri='dummy:a'), Track(uri='dummy:b')])
self.assertEqual(self.core.playback.state.get(), STOPPED) self.assertEqual(self.core.playback.state.get(), STOPPED)
self.mpris.OpenUri('dummy:/test/uri') 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): def test_open_uri_starts_playback_of_new_track_if_paused(self):
self.mpris.get_CanPlay = lambda *_: True self.mpris.get_CanPlay = lambda *_: True
self.backend.library.dummy_library = [Track(uri='dummy:/test/uri')] self.backend.library.dummy_library = [Track(uri='dummy:/test/uri')]
self.core.tracklist.append([ self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')])
Track(uri='dummy:a'), Track(uri='dummy:b')])
self.core.playback.play() self.core.playback.play()
self.core.playback.pause() self.core.playback.pause()
self.assertEqual(self.core.playback.state.get(), PAUSED) 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): def test_open_uri_starts_playback_of_new_track_if_playing(self):
self.mpris.get_CanPlay = lambda *_: True self.mpris.get_CanPlay = lambda *_: True
self.backend.library.dummy_library = [Track(uri='dummy:/test/uri')] self.backend.library.dummy_library = [Track(uri='dummy:/test/uri')]
self.core.tracklist.append([ self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')])
Track(uri='dummy:a'), Track(uri='dummy:b')])
self.core.playback.play() self.core.playback.play()
self.assertEqual(self.core.playback.state.get(), PLAYING) self.assertEqual(self.core.playback.state.get(), PLAYING)
self.assertEqual(self.core.playback.current_track.get().uri, 'dummy:a') self.assertEqual(self.core.playback.current_track.get().uri, 'dummy:a')

View File

@ -44,7 +44,7 @@ class PlayerInterfaceTest(unittest.TestCase):
pykka.ActorRegistry.stop_all() pykka.ActorRegistry.stop_all()
def test_activate_playlist_appends_tracks_to_tracklist(self): 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-a'),
Track(uri='dummy:old-b'), Track(uri='dummy:old-b'),
]) ])

View File

@ -30,5 +30,6 @@ class VersionTest(unittest.TestCase):
self.assertLess(SV('0.7.1'), SV('0.7.2')) 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.2'), SV('0.7.3'))
self.assertLess(SV('0.7.3'), SV('0.8.0')) self.assertLess(SV('0.7.3'), SV('0.8.0'))
self.assertLess(SV('0.8.0'), SV(__version__)) self.assertLess(SV('0.8.0'), SV('0.8.1'))
self.assertLess(SV(__version__), SV('0.8.2')) self.assertLess(SV('0.8.1'), SV(__version__))
self.assertLess(SV(__version__), SV('0.9.1'))