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.
v0.9.0 (in development)
=======================
**Multiple backends support**
v0.9.0 (2012-11-21)
===================
Support for using the local and Spotify backends simultaneously have for a very
long time been our most requested feature. Finally, it's here!
**Dependencies**
- pyspotify >= 1.9, < 1.10 is now required for Spotify support.
**Documentation**
- New :ref:`installation` guides, organized by OS and distribution so that you
can follow one concise list of instructions instead of jumping around the
docs to look for instructions for each dependency.
- Moved :ref:`raspberrypi-installation` howto from the wiki to the docs.
- Updated :ref:`mpd-clients` overview.
- Added :ref:`mpris-clients` and :ref:`upnp-clients` overview.
**Multiple backends support**
- Both the local backend and the Spotify backend are now turned on by default.
The local backend is listed first in the :attr:`mopidy.settings.BACKENDS`
setting, and are thus given the highest priority in e.g. search results,
@ -24,8 +40,85 @@ long time been our most requested feature. Finally, it's here!
As always, see :mod:`mopidy.settings` for the full list of available
settings.
**Spotify backend**
- The Spotify backend now includes release year and artist on albums.
- :issue:`233`: The Spotify backend now returns the track if you search for the
Spotify track URI.
- Added support for connecting to the Spotify service through an HTTP or SOCKS
proxy, which is supported by pyspotify >= 1.9.
- Subscriptions to other Spotify user's "starred" playlists are ignored, as
they currently isn't fully supported by pyspotify.
**Local backend**
- :issue:`236`: The ``mopidy-scan`` command failed to include tags from ALAC
files (Apple lossless) because it didn't support multiple tag messages from
GStreamer per track it scanned.
- Added support for search by filename to local backend.
**MPD frontend**
- :issue:`218`: The MPD commands ``listplaylist`` and ``listplaylistinfo`` now
accepts unquoted playlist names if they don't contain spaces.
- :issue:`246`: The MPD command ``list album artist ""`` and similar
``search``, ``find``, and ``list`` commands with empty filter values caused a
:exc:`LookupError`, but should have been ignored by the MPD server.
- The MPD frontend no longer lowercases search queries. This broke e.g. search
by URI, where casing may be essential.
- The MPD command ``plchanges`` always returned the entire playlist. It now
returns an empty response when the client has seen the latest version.
- The MPD commands ``search`` and ``find`` now allows the key ``file``, which
is used by ncmpcpp instead of ``filename``.
- The MPD commands ``search`` and ``find`` now allow search query values to be
empty strings.
- The MPD command ``listplaylists`` will no longer return playlists without a
name. This could crash ncmpcpp.
- The MPD command ``list`` will no longer return artist names, album names, or
dates that are blank.
- The MPD command ``decoders`` will now return an empty response instead of a
"not implemented" error to make the ncmpcpp browse view work the first time
it is opened.
**MPRIS frontend**
- The MPRIS playlists interface is now supported by our MPRIS frontend. This
means that you now can select playlists to queue and play from the Ubuntu
Sound Menu.
**Audio mixers**
- Made the :mod:`NAD mixer <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
backends:
backends and the future HTTP frontend.
- A new layer and actor, "core", has been added to our stack, inbetween the
frontends and the backends. The responsibility of the core layer and actor is
@ -36,12 +129,6 @@ backends:
Frontends no longer know anything about the backends. They just use the
:ref:`core-api`.
- The base playback provider has been updated with sane default behavior
instead of empty functions. By default, the playback provider now lets
GStreamer keep track of the current track's time position. The local backend
simply uses the base playback provider without any changes. The same applies
to any future backend that just needs GStreamer to play an URI for it.
- The dependency graph between the core controllers and the backend providers
have been straightened out, so that we don't have any circular dependencies.
The frontend, core, backend, and audio layers are now strictly separate. The
@ -51,118 +138,113 @@ backends:
broadcasting of events to listeners, through e.g.
:class:`mopidy.core.CoreListener` and :class:`mopidy.audio.AudioListener`.
See :ref:`concepts` for more details and illustrations of all the relations.
- All dependencies are now explicitly passed to the constructors of the
frontends, core, and the backends. This makes testing each layer with
dummy/mocked lower layers easier than with the old variant, where
dependencies where looked up in Pykka's actor registry.
- Renamed "current playlist" to "tracklist" everywhere, including the core API
used by frontends.
- All properties in the core API now got getters, and setters if setting them
is allowed. They are not explictly listed in the docs as they have the same
behavior as the documented properties, but they are available and may be
used. This is useful for the future HTTP frontend.
- Renamed "stored playlists" to "playlists" everywhere, including the core API
used by frontends.
- The playlists part of the core API has been revised to be more focused around
the playlist URI, and some redundant functionality has been removed:
- :attr:`mopidy.core.PlaylistsController.playlists` no longer supports
assignment to it. The `playlists` property on the backend layer still does,
and all functionality is maintained by assigning to the playlists
collections at the backend level.
- :meth:`mopidy.core.PlaylistsController.delete` now accepts an URI, and not
a playlist object.
- :meth:`mopidy.core.PlaylistsController.save` now returns the saved
playlist. The returned playlist may differ from the saved playlist, and
should thus be used instead of the playlist passed to ``save()``.
- :meth:`mopidy.core.PlaylistsController.rename` has been removed, since
renaming can be done with ``save()``.
**Changes**
- Made the :mod:`NAD mixer <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.
*Models:*
- Added :attr:`mopidy.models.Album.date` attribute. It has the same format as
the existing :attr:`mopidy.models.Track.date`.
- The Spotify backend now includes release year and artist on albums.
- Added :class:`mopidy.models.ModelJSONEncoder` and
:func:`mopidy.models.model_json_decoder` for automatic JSON serialization and
deserialization of data structures which contains Mopidy models. This is
useful for the future HTTP frontend.
- Added support for search by filename to local backend.
*Library:*
- Added optional background thread for debugging deadlocks. When the feature is
enabled via the ``--debug-thread`` option or
:attr:`mopidy.settings.DEBUG_THREAD` setting a ``SIGUSR1`` signal will dump
the traceback for all running threads.
- :meth:`mopidy.core.LibraryController.find_exact` and
:meth:`mopidy.core.LibraryController.search` now returns plain lists of
tracks instead of playlist objects.
- Make the entire code base use unicode strings by default, and only fall back
to bytestrings where it is required. Another step closer to Python 3.
- :meth:`mopidy.core.LibraryController.lookup` now returns a list of tracks
instead of a single track. This makes it possible to support lookup of
artist or album URIs which then can expand to a list of tracks.
- The settings validator will now allow any setting prefixed with ``CUSTOM_``
to exist in the settings file.
*Playback:*
- The MPD commands ``search`` and ``find`` now allows the key ``file``, which
is used by ncmpcpp instead of ``filename``.
- The base playback provider has been updated with sane default behavior
instead of empty functions. By default, the playback provider now lets
GStreamer keep track of the current track's time position. The local backend
simply uses the base playback provider without any changes. Any future
backend that just feeds URIs to GStreamer to play can also use the base
playback provider without any changes.
- The Spotify backend now returns the track if you search for the Spotify track
URI. (Fixes: :issue:`233`)
- Removed :attr:`mopidy.core.PlaybackController.track_at_previous`. Use
:attr:`mopidy.core.PlaybackController.tl_track_at_previous` instead.
- :meth:`mopidy.core.TracklistController.append` now returns a list of the
:class:`mopidy.models.TlTrack` instances that was added to the tracklist.
This makes it easier to start playing one of the tracks that was just
appended to the tracklist.
- Removed :attr:`mopidy.core.PlaybackController.track_at_next`. Use
:attr:`mopidy.core.PlaybackController.tl_track_at_next` instead.
- Removed :attr:`mopidy.core.PlaybackController.track_at_eot`. Use
:attr:`mopidy.core.PlaybackController.tl_track_at_eot` instead.
- Removed :attr:`mopidy.core.PlaybackController.current_tlid`. Use
:attr:`mopidy.core.PlaybackController.current_tl_track` instead.
*Playlists:*
The playlists part of the core API has been revised to be more focused around
the playlist URI, and some redundant functionality has been removed:
- Renamed "stored playlists" to "playlists" everywhere, including the core API
used by frontends.
- :attr:`mopidy.core.PlaylistsController.playlists` no longer supports
assignment to it. The `playlists` property on the backend layer still does,
and all functionality is maintained by assigning to the playlists collections
at the backend level.
- :meth:`mopidy.core.PlaylistsController.delete` now accepts an URI, and not a
playlist object.
- :meth:`mopidy.core.PlaylistsController.save` now returns the saved playlist.
The returned playlist may differ from the saved playlist, and should thus be
used instead of the playlist passed to
:meth:`mopidy.core.PlaylistsController.save`.
- :meth:`mopidy.core.PlaylistsController.rename` has been removed, since
renaming can be done with :meth:`mopidy.core.PlaylistsController.save`.
- :meth:`mopidy.core.PlaylistsController.get` has been replaced by
:meth:`mopidy.core.PlaylistsController.filter`.
- The event :meth:`mopidy.core.CoreListener.playlist_changed` has been changed
to include the playlist that was changed.
*Tracklist:*
- Renamed "current playlist" to "tracklist" everywhere, including the core API
used by frontends.
- Removed :meth:`mopidy.core.TracklistController.append`. Use
:meth:`mopidy.core.TracklistController.add` instead, which is now capable of
adding multiple tracks.
- :meth:`mopidy.core.TracklistController.get` has been replaced by
:meth:`mopidy.core.TracklistController.filter`.
- :meth:`mopidy.core.TracklistController.remove` can now remove multiple
tracks, and returns the tracks it removed.
- When the tracklist is changed, we now trigger the new
:meth:`mopidy.core.CoreListener.tracklist_changed` event. Previously we
triggered :meth:`mopidy.core.CoreListener.playlist_changed`, which is
intended for stored playlists, not the tracklist.
- The event :meth:`mopidy.core.CoreListener.playlist_changed` has been changed
to include the playlist that was changed.
*Towards Python 3 support:*
- The MPRIS playlists interface is now supported by our MPRIS frontend. This
means that you now can select playlists to queue and play from the Ubuntu
Sound Menu.
- :meth:`mopidy.core.LibraryController.find_exact` and
:meth:`mopidy.core.LibraryController.search` now returns plain lists of
tracks instead of playlist objects.
- :meth:`mopidy.core.TracklistController.get` has been replaced by
:meth:`mopidy.core.TracklistController.filter`.
- :meth:`mopidy.core.PlaylistsController.get` has been replaced by
:meth:`mopidy.core.PlaylistsController.filter`.
- :meth:`mopidy.core.TracklistController.remove` can now remove multiple
tracks, and returns the tracks it removed.
- :meth:`mopidy.core.LibraryController.lookup` now returns a list of tracks.
This makes it possible to support lookup of artist or album URIs which then
can expand to a list of tracks.
**Bug fixes**
- :issue:`218`: The MPD commands ``listplaylist`` and ``listplaylistinfo`` now
accepts unquotes playlist names if they don't contain spaces.
- The MPD command ``plchanges`` always returned the entire playlist. It now
returns an empty response when the client has seen the latest version.
- MPD no longer lowercases search queries. This broke e.g. search by URI, where
casing may be essential.
- :issue:`236`: The ``mopidy-scan`` command failed to include tags from ALAC
files (Apple lossless) because it didn't support multiple tag messages from
GStreamer per track it scanned.
- :issue:`246`: The MPD command ``list album artist ""`` and similar
``search``, ``find``, and ``list`` commands with empty filter values caused a
:exc:`LookupError`, but should have been ignored by the MPD server.
- Make the entire code base use unicode strings by default, and only fall back
to bytestrings where it is required. Another step closer to Python 3.
v0.8.1 (2012-10-30)

View File

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

View File

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

View File

@ -21,7 +21,7 @@ https://github.com/mopidy/mopidy/issues?labels=Spotify+backend
**Dependencies:**
- libspotify >= 12, < 13 (libspotify12 package from apt.mopidy.com)
- pyspotify >= 1.8, < 1.9 (python-spotify package from apt.mopidy.com)
- pyspotify >= 1.9, < 1.10 (python-spotify package from apt.mopidy.com)
**Settings:**

View File

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

View File

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

View File

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

View File

@ -56,6 +56,10 @@ def to_mopidy_playlist(spotify_playlist):
uri = str(Link.from_playlist(spotify_playlist))
if not spotify_playlist.is_loaded():
return Playlist(uri=uri, name='[loading...]')
if not spotify_playlist.name():
# Other user's "starred" playlists isn't handled properly by pyspotify
# See https://github.com/mopidy/pyspotify/issues/81
return
return Playlist(
uri=uri,
name=spotify_playlist.name(),

View File

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

View File

@ -12,56 +12,12 @@ from . import listener
logger = logging.getLogger('mopidy.core')
def option_wrapper(name, default):
def get_option(self):
return getattr(self, name, default)
def set_option(self, value):
if getattr(self, name, default) != value:
# pylint: disable = W0212
self._trigger_options_changed()
# pylint: enable = W0212
return setattr(self, name, value)
return property(get_option, set_option)
class PlaybackController(object):
# pylint: disable = R0902
# Too many instance attributes
pykka_traversable = True
#: :class:`True`
#: Tracks are removed from the playlist when they have been played.
#: :class:`False`
#: Tracks are not removed from the playlist.
consume = option_wrapper('_consume', False)
#: The currently playing or selected :class:`mopidy.models.TlTrack`, or
#: :class:`None`.
current_tl_track = None
#: :class:`True`
#: Tracks are selected at random from the playlist.
#: :class:`False`
#: Tracks are played in the order of the playlist.
random = option_wrapper('_random', False)
#: :class:`True`
#: The current playlist is played repeatedly. To repeat a single track,
#: select both :attr:`repeat` and :attr:`single`.
#: :class:`False`
#: The current playlist is played once.
repeat = option_wrapper('_repeat', False)
#: :class:`True`
#: Playback is stopped after current song, unless in :attr:`repeat`
#: mode.
#: :class:`False`
#: Playback continues after current song.
single = option_wrapper('_single', False)
def __init__(self, audio, backends, core):
self.audio = audio
self.backends = backends
@ -79,42 +35,128 @@ class PlaybackController(object):
uri_scheme = urlparse.urlparse(uri).scheme
return self.backends.with_playback_by_uri_scheme.get(uri_scheme, None)
def _get_tlid(self, tl_track):
if tl_track is None:
return None
return tl_track.tlid
### Properties
def _get_track(self, tl_track):
if tl_track is None:
return None
return tl_track.track
def get_consume(self):
return getattr(self, '_consume', False)
@property
def current_tlid(self):
"""
The TLID (tracklist ID) of the currently playing or selected
track.
def set_consume(self, value):
if self.get_consume() != value:
self._trigger_options_changed()
return setattr(self, '_consume', value)
Read-only. Extracted from :attr:`current_tl_track` for convenience.
"""
return self._get_tlid(self.current_tl_track)
consume = property(get_consume, set_consume)
"""
:class:`True`
Tracks are removed from the playlist when they have been played.
:class:`False`
Tracks are not removed from the playlist.
"""
@property
def current_track(self):
"""
The currently playing or selected :class:`mopidy.models.Track`.
current_tl_track = None
"""
The currently playing or selected :class:`mopidy.models.TlTrack`, or
:class:`None`.
"""
Read-only. Extracted from :attr:`current_tl_track` for convenience.
"""
return self._get_track(self.current_tl_track)
def get_current_track(self):
return self.current_tl_track and self.current_tl_track.track
@property
def tracklist_position(self):
"""
The position of the current track in the tracklist.
current_track = property(get_current_track)
"""
The currently playing or selected :class:`mopidy.models.Track`.
Read-only.
"""
Read-only. Extracted from :attr:`current_tl_track` for convenience.
"""
def get_random(self):
return getattr(self, '_random', False)
def set_random(self, value):
if self.get_random() != value:
self._trigger_options_changed()
return setattr(self, '_random', value)
random = property(get_random, set_random)
"""
:class:`True`
Tracks are selected at random from the playlist.
:class:`False`
Tracks are played in the order of the playlist.
"""
def get_repeat(self):
return getattr(self, '_repeat', False)
def set_repeat(self, value):
if self.get_repeat() != value:
self._trigger_options_changed()
return setattr(self, '_repeat', value)
repeat = property(get_repeat, set_repeat)
"""
:class:`True`
The current playlist is played repeatedly. To repeat a single track,
select both :attr:`repeat` and :attr:`single`.
:class:`False`
The current playlist is played once.
"""
def get_single(self):
return getattr(self, '_single', False)
def set_single(self, value):
if self.get_single() != value:
self._trigger_options_changed()
return setattr(self, '_single', value)
single = property(get_single, set_single)
"""
:class:`True`
Playback is stopped after current song, unless in :attr:`repeat`
mode.
:class:`False`
Playback continues after current song.
"""
def get_state(self):
return self._state
def set_state(self, new_state):
(old_state, self._state) = (self.state, new_state)
logger.debug('Changing state: %s -> %s', old_state, new_state)
self._trigger_playback_state_changed(old_state, new_state)
state = property(get_state, set_state)
"""
The playback state. Must be :attr:`PLAYING`, :attr:`PAUSED`, or
:attr:`STOPPED`.
Possible states and transitions:
.. digraph:: state_transitions
"STOPPED" -> "PLAYING" [ label="play" ]
"STOPPED" -> "PAUSED" [ label="pause" ]
"PLAYING" -> "STOPPED" [ label="stop" ]
"PLAYING" -> "PAUSED" [ label="pause" ]
"PLAYING" -> "PLAYING" [ label="play" ]
"PAUSED" -> "PLAYING" [ label="resume" ]
"PAUSED" -> "STOPPED" [ label="stop" ]
"""
def get_time_position(self):
backend = self._get_backend()
if backend:
return backend.playback.get_time_position().get()
else:
return 0
time_position = property(get_time_position)
"""Time position in milliseconds."""
def get_tracklist_position(self):
if self.current_tl_track is None:
return None
try:
@ -122,25 +164,14 @@ class PlaybackController(object):
except ValueError:
return None
@property
def track_at_eot(self):
"""
The track that will be played at the end of the current track.
tracklist_position = property(get_tracklist_position)
"""
The position of the current track in the tracklist.
Read-only. A :class:`mopidy.models.Track` extracted from
:attr:`tl_track_at_eot` for convenience.
"""
return self._get_track(self.tl_track_at_eot)
Read-only.
"""
@property
def tl_track_at_eot(self):
"""
The track that will be played at the end of the current track.
Read-only. A :class:`mopidy.models.TlTrack`.
Not necessarily the same track as :attr:`tl_track_at_next`.
"""
def get_tl_track_at_eot(self):
# pylint: disable = R0911
# Too many return statements
@ -173,28 +204,16 @@ class PlaybackController(object):
except IndexError:
return None
@property
def track_at_next(self):
"""
The track that will be played if calling :meth:`next()`.
tl_track_at_eot = property(get_tl_track_at_eot)
"""
The track that will be played at the end of the current track.
Read-only. A :class:`mopidy.models.Track` extracted from
:attr:`tl_track_at_next` for convenience.
"""
return self._get_track(self.tl_track_at_next)
Read-only. A :class:`mopidy.models.TlTrack`.
@property
def tl_track_at_next(self):
"""
The track that will be played if calling :meth:`next()`.
Not necessarily the same track as :attr:`tl_track_at_next`.
"""
Read-only. A :class:`mopidy.models.TlTrack`.
For normal playback this is the next track in the playlist. If repeat
is enabled the next track can loop around the playlist. When random is
enabled this should be a random track, all tracks should be played once
before the list repeats.
"""
def get_tl_track_at_next(self):
tl_tracks = self.core.tracklist.tl_tracks
if not tl_tracks:
@ -221,27 +240,19 @@ class PlaybackController(object):
except IndexError:
return None
@property
def track_at_previous(self):
"""
The track that will be played if calling :meth:`previous()`.
tl_track_at_next = property(get_tl_track_at_next)
"""
The track that will be played if calling :meth:`next()`.
Read-only. A :class:`mopidy.models.Track` extracted from
:attr:`tl_track_at_previous` for convenience.
"""
return self._get_track(self.tl_track_at_previous)
Read-only. A :class:`mopidy.models.TlTrack`.
@property
def tl_track_at_previous(self):
"""
The track that will be played if calling :meth:`previous()`.
For normal playback this is the next track in the playlist. If repeat
is enabled the next track can loop around the playlist. When random is
enabled this should be a random track, all tracks should be played once
before the list repeats.
"""
A :class:`mopidy.models.TlTrack`.
For normal playback this is the previous track in the playlist. If
random and/or consume is enabled it should return the current track
instead.
"""
def get_tl_track_at_previous(self):
if self.repeat or self.consume or self.random:
return self.current_tl_track
@ -250,59 +261,36 @@ class PlaybackController(object):
return self.core.tracklist.tl_tracks[self.tracklist_position - 1]
@property
def state(self):
"""
The playback state. Must be :attr:`PLAYING`, :attr:`PAUSED`, or
:attr:`STOPPED`.
tl_track_at_previous = property(get_tl_track_at_previous)
"""
The track that will be played if calling :meth:`previous()`.
Possible states and transitions:
A :class:`mopidy.models.TlTrack`.
.. digraph:: state_transitions
For normal playback this is the previous track in the playlist. If
random and/or consume is enabled it should return the current track
instead.
"""
"STOPPED" -> "PLAYING" [ label="play" ]
"STOPPED" -> "PAUSED" [ label="pause" ]
"PLAYING" -> "STOPPED" [ label="stop" ]
"PLAYING" -> "PAUSED" [ label="pause" ]
"PLAYING" -> "PLAYING" [ label="play" ]
"PAUSED" -> "PLAYING" [ label="resume" ]
"PAUSED" -> "STOPPED" [ label="stop" ]
"""
return self._state
@state.setter # noqa
def state(self, new_state):
(old_state, self._state) = (self.state, new_state)
logger.debug('Changing state: %s -> %s', old_state, new_state)
self._trigger_playback_state_changed(old_state, new_state)
@property
def time_position(self):
"""Time position in milliseconds."""
backend = self._get_backend()
if backend:
return backend.playback.get_time_position().get()
else:
return 0
@property
def volume(self):
"""Volume as int in range [0..100] or :class:`None`"""
def get_volume(self):
if self.audio:
return self.audio.get_volume().get()
else:
# For testing
return self._volume
@volume.setter # noqa
def volume(self, volume):
def set_volume(self, volume):
if self.audio:
self.audio.set_volume(volume)
else:
# For testing
self._volume = volume
volume = property(get_volume, set_volume)
"""Volume as int in range [0..100] or :class:`None`"""
### Methods
def change_track(self, tl_track, on_error_step=1):
"""
Change to the given track, keeping the current playback state.
@ -324,6 +312,8 @@ class PlaybackController(object):
def on_end_of_track(self):
"""
Tell the playback controller that end of track is reached.
Used by event handler in :class:`mopidy.core.Core`.
"""
if self.state == PlaybackState.STOPPED:
return
@ -343,7 +333,7 @@ class PlaybackController(object):
"""
Tell the playback controller that the current playlist has changed.
Used by :class:`mopidy.core.CurrentPlaylistController`.
Used by :class:`mopidy.core.TracklistController`.
"""
self._first_shuffle = True
self._shuffled = []

View File

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

View File

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

View File

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

View File

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

View File

@ -52,7 +52,7 @@ def count(context, tag, needle):
@handle_request(
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):
"""
*musicpd.org, music database section:*
@ -250,7 +250,8 @@ def _list_artist(context, query):
tracks = context.core.library.find_exact(**query).get()
for track in tracks:
for artist in track.artists:
artists.add(('Artist', artist.name))
if artist.name:
artists.add(('Artist', artist.name))
return artists
@ -258,7 +259,7 @@ def _list_album(context, query):
albums = set()
tracks = context.core.library.find_exact(**query).get()
for track in tracks:
if track.album is not None:
if track.album and track.album.name:
albums.add(('Album', track.album.name))
return albums
@ -267,7 +268,7 @@ def _list_date(context, query):
dates = set()
tracks = context.core.library.find_exact(**query).get()
for track in tracks:
if track.date is not None:
if track.date:
dates.add(('Date', track.date))
return dates
@ -334,7 +335,7 @@ def rescan(context, uri=None):
@handle_request(
r'^search (?P<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):
"""
*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.
"""
if context.core.playback.tracklist_position != songpos:
if context.core.playback.tracklist_position.get() != songpos:
playpos(context, songpos)
context.core.playback.seek(int(seconds) * 1000)
context.core.playback.seek(int(seconds) * 1000).get()
@handle_request(r'^seekid "(?P<tlid>\d+)" "(?P<seconds>\d+)"$')
@ -343,9 +343,10 @@ def seekid(context, tlid, seconds):
Seeks to the position ``TIME`` (in seconds) of song ``SONGID``.
"""
if context.core.playback.current_tlid != tlid:
tl_track = context.core.playback.current_tl_track.get()
if not tl_track or tl_track.tlid != tlid:
playid(context, tlid)
context.core.playback.seek(int(seconds) * 1000)
context.core.playback.seek(int(seconds) * 1000).get()
@handle_request(r'^setvol (?P<volume>[-+]*\d+)$')

View File

@ -1,7 +1,6 @@
from __future__ import unicode_literals
from mopidy.frontends.mpd.protocol import handle_request, mpd_commands
from mopidy.frontends.mpd.exceptions import MpdNotImplemented
@handle_request(r'^commands$', auth_required=False)
@ -47,8 +46,15 @@ def decoders(context):
mime_type: audio/mpeg
plugin: mpcdec
suffix: mpc
*Clarifications:*
- ncmpcpp asks for decoders the first time you open the browse view. By
returning nothing and OK instead of an not implemented error, we avoid
"Not implemented" showing up in the ncmpcpp interface, and we get the
list of playlists without having to enter the browse interface twice.
"""
raise MpdNotImplemented # TODO
return # TODO
@handle_request(r'^notcommands$', auth_required=False)

View File

@ -7,7 +7,7 @@ from mopidy.frontends.mpd.protocol import handle_request
from mopidy.frontends.mpd.translator import playlist_to_mpd_format
@handle_request(r'^listplaylist (?P<name>\S+)$')
@handle_request(r'^listplaylist (?P<name>\w+)$')
@handle_request(r'^listplaylist "(?P<name>[^"]+)"$')
def listplaylist(context, name):
"""
@ -29,7 +29,7 @@ def listplaylist(context, name):
return ['file: %s' % t.uri for t in playlists[0].tracks]
@handle_request(r'^listplaylistinfo (?P<name>\S+)$')
@handle_request(r'^listplaylistinfo (?P<name>\w+)$')
@handle_request(r'^listplaylistinfo "(?P<name>[^"]+)"$')
def listplaylistinfo(context, name):
"""
@ -70,9 +70,16 @@ def listplaylists(context):
Last-Modified: 2010-02-06T02:10:25Z
playlist: b
Last-Modified: 2010-02-06T02:11:08Z
*Clarifications:*
- ncmpcpp 0.5.10 segfaults if we return 'playlist: ' on a line, so we must
ignore playlists without names, which isn't very useful anyway.
"""
result = []
for playlist in context.core.playlists.playlists.get():
if not playlist.name:
continue
result.append(('playlist', playlist.name))
last_modified = (
playlist.last_modified or dt.datetime.now()).isoformat()
@ -101,7 +108,7 @@ def load(context, name):
playlists = context.core.playlists.filter(name=name).get()
if not playlists:
raise MpdNoExistError('No such playlist', command='load')
context.core.tracklist.append(playlists[0].tracks)
context.core.tracklist.add(playlists[0].tracks)
@handle_request(r'^playlistadd "(?P<name>[^"]+)" "(?P<uri>[^"]+)"$')

View File

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

View File

@ -251,3 +251,34 @@ SPOTIFY_PASSWORD = ''
#:
#: SPOTIFY_BITRATE = 160
SPOTIFY_BITRATE = 160
#: Spotify proxy host.
#:
#: Used by :mod:`mopidy.backends.spotify`.
#:
#: Example::
#:
#: SPOTIFY_PROXY_HOST = u'protocol://host:port'
#:
#: Default::
#:
#: SPOTIFY_PROXY_HOST = None
SPOTIFY_PROXY_HOST = None
#: Spotify proxy username.
#:
#: Used by :mod:`mopidy.backends.spotify`.
#:
#: Default::
#:
#: SPOTIFY_PROXY_USERNAME = None
SPOTIFY_PROXY_USERNAME = None
#: Spotify proxy password.
#:
#: Used by :mod:`mopidy.backends.spotify`
#:
#: Default::
#:
#: SPOTIFY_PROXY_PASSWORD = None
SPOTIFY_PROXY_PASSWORD = None

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
def populate_playlist(func):
def populate_tracklist(func):
def wrapper(self):
for track in self.tracks:
self.core.tracklist.add(track)
self.tl_tracks = self.core.tracklist.add(self.tracks)
return func(self)
wrapper.__name__ = func.__name__

View File

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

View File

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

View File

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

View File

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

View File

@ -35,7 +35,7 @@ class CorePlaybackTest(unittest.TestCase):
self.core = Core(audio=None, backends=[
self.backend1, self.backend2, self.backend3])
self.core.tracklist.append(self.tracks)
self.core.tracklist.add(self.tracks)
self.tl_tracks = self.core.tracklist.tl_tracks
self.unplayable_tl_track = self.tl_tracks[2]

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -131,21 +131,21 @@ class StatusHandlerTest(unittest.TestCase):
self.assertEqual(result['state'], 'pause')
def test_status_method_when_playlist_loaded_contains_song(self):
self.core.tracklist.append([Track(uri='dummy:a')])
self.core.tracklist.add([Track(uri='dummy:a')])
self.core.playback.play()
result = dict(status.status(self.context))
self.assertIn('song', result)
self.assertGreaterEqual(int(result['song']), 0)
def test_status_method_when_playlist_loaded_contains_tlid_as_songid(self):
self.core.tracklist.append([Track(uri='dummy:a')])
self.core.tracklist.add([Track(uri='dummy:a')])
self.core.playback.play()
result = dict(status.status(self.context))
self.assertIn('songid', result)
self.assertEqual(int(result['songid']), 0)
def test_status_method_when_playing_contains_time_with_no_length(self):
self.core.tracklist.append([Track(uri='dummy:a', length=None)])
self.core.tracklist.add([Track(uri='dummy:a', length=None)])
self.core.playback.play()
result = dict(status.status(self.context))
self.assertIn('time', result)
@ -155,7 +155,7 @@ class StatusHandlerTest(unittest.TestCase):
self.assertLessEqual(position, total)
def test_status_method_when_playing_contains_time_with_length(self):
self.core.tracklist.append([Track(uri='dummy:a', length=10000)])
self.core.tracklist.add([Track(uri='dummy:a', length=10000)])
self.core.playback.play()
result = dict(status.status(self.context))
self.assertIn('time', result)
@ -165,7 +165,7 @@ class StatusHandlerTest(unittest.TestCase):
self.assertLessEqual(position, total)
def test_status_method_when_playing_contains_elapsed(self):
self.core.tracklist.append([Track(uri='dummy:a', length=60000)])
self.core.tracklist.add([Track(uri='dummy:a', length=60000)])
self.core.playback.play()
self.core.playback.pause()
self.core.playback.seek(59123)
@ -174,7 +174,7 @@ class StatusHandlerTest(unittest.TestCase):
self.assertEqual(result['elapsed'], '59.123')
def test_status_method_when_starting_playing_contains_elapsed_zero(self):
self.core.tracklist.append([Track(uri='dummy:a', length=10000)])
self.core.tracklist.add([Track(uri='dummy:a', length=10000)])
self.core.playback.play()
self.core.playback.pause()
result = dict(status.status(self.context))
@ -182,7 +182,7 @@ class StatusHandlerTest(unittest.TestCase):
self.assertEqual(result['elapsed'], '0.000')
def test_status_method_when_playing_contains_bitrate(self):
self.core.tracklist.append([Track(uri='dummy:a', bitrate=320)])
self.core.tracklist.add([Track(uri='dummy:a', bitrate=320)])
self.core.playback.play()
result = dict(status.status(self.context))
self.assertIn('bitrate', result)

View File

@ -101,16 +101,14 @@ class PlayerInterfaceTest(unittest.TestCase):
def test_set_rate_is_ignored_if_can_control_is_false(self):
self.mpris.get_CanControl = lambda *_: False
self.core.tracklist.append([
Track(uri='dummy:a'), Track(uri='dummy:b')])
self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')])
self.core.playback.play()
self.assertEqual(self.core.playback.state.get(), PLAYING)
self.mpris.Set(objects.PLAYER_IFACE, 'Rate', 0)
self.assertEqual(self.core.playback.state.get(), PLAYING)
def test_set_rate_to_zero_pauses_playback(self):
self.core.tracklist.append([
Track(uri='dummy:a'), Track(uri='dummy:b')])
self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')])
self.core.playback.play()
self.assertEqual(self.core.playback.state.get(), PLAYING)
self.mpris.Set(objects.PLAYER_IFACE, 'Rate', 0)
@ -150,7 +148,7 @@ class PlayerInterfaceTest(unittest.TestCase):
self.assertEqual(result['mpris:trackid'], '')
def test_get_metadata_has_trackid_based_on_tlid(self):
self.core.tracklist.append([Track(uri='dummy:a')])
self.core.tracklist.add([Track(uri='dummy:a')])
self.core.playback.play()
(tlid, track) = self.core.playback.current_tl_track.get()
result = self.mpris.Get(objects.PLAYER_IFACE, 'Metadata')
@ -159,28 +157,28 @@ class PlayerInterfaceTest(unittest.TestCase):
result['mpris:trackid'], '/com/mopidy/track/%d' % tlid)
def test_get_metadata_has_track_length(self):
self.core.tracklist.append([Track(uri='dummy:a', length=40000)])
self.core.tracklist.add([Track(uri='dummy:a', length=40000)])
self.core.playback.play()
result = self.mpris.Get(objects.PLAYER_IFACE, 'Metadata')
self.assertIn('mpris:length', result.keys())
self.assertEqual(result['mpris:length'], 40000000)
def test_get_metadata_has_track_uri(self):
self.core.tracklist.append([Track(uri='dummy:a')])
self.core.tracklist.add([Track(uri='dummy:a')])
self.core.playback.play()
result = self.mpris.Get(objects.PLAYER_IFACE, 'Metadata')
self.assertIn('xesam:url', result.keys())
self.assertEqual(result['xesam:url'], 'dummy:a')
def test_get_metadata_has_track_title(self):
self.core.tracklist.append([Track(name='a')])
self.core.tracklist.add([Track(name='a')])
self.core.playback.play()
result = self.mpris.Get(objects.PLAYER_IFACE, 'Metadata')
self.assertIn('xesam:title', result.keys())
self.assertEqual(result['xesam:title'], 'a')
def test_get_metadata_has_track_artists(self):
self.core.tracklist.append([Track(artists=[
self.core.tracklist.add([Track(artists=[
Artist(name='a'), Artist(name='b'), Artist(name=None)])])
self.core.playback.play()
result = self.mpris.Get(objects.PLAYER_IFACE, 'Metadata')
@ -188,14 +186,14 @@ class PlayerInterfaceTest(unittest.TestCase):
self.assertEqual(result['xesam:artist'], ['a', 'b'])
def test_get_metadata_has_track_album(self):
self.core.tracklist.append([Track(album=Album(name='a'))])
self.core.tracklist.add([Track(album=Album(name='a'))])
self.core.playback.play()
result = self.mpris.Get(objects.PLAYER_IFACE, 'Metadata')
self.assertIn('xesam:album', result.keys())
self.assertEqual(result['xesam:album'], 'a')
def test_get_metadata_has_track_album_artists(self):
self.core.tracklist.append([Track(album=Album(artists=[
self.core.tracklist.add([Track(album=Album(artists=[
Artist(name='a'), Artist(name='b'), Artist(name=None)]))])
self.core.playback.play()
result = self.mpris.Get(objects.PLAYER_IFACE, 'Metadata')
@ -203,7 +201,7 @@ class PlayerInterfaceTest(unittest.TestCase):
self.assertEqual(result['xesam:albumArtist'], ['a', 'b'])
def test_get_metadata_has_track_number_in_album(self):
self.core.tracklist.append([Track(track_no=7)])
self.core.tracklist.add([Track(track_no=7)])
self.core.playback.play()
result = self.mpris.Get(objects.PLAYER_IFACE, 'Metadata')
self.assertIn('xesam:trackNumber', result.keys())
@ -246,7 +244,7 @@ class PlayerInterfaceTest(unittest.TestCase):
self.assertEqual(self.core.playback.volume.get(), 10)
def test_get_position_returns_time_position_in_microseconds(self):
self.core.tracklist.append([Track(uri='dummy:a', length=40000)])
self.core.tracklist.add([Track(uri='dummy:a', length=40000)])
self.core.playback.play()
self.core.playback.seek(10000)
result_in_microseconds = self.mpris.Get(
@ -270,15 +268,14 @@ class PlayerInterfaceTest(unittest.TestCase):
def test_can_go_next_is_true_if_can_control_and_other_next_track(self):
self.mpris.get_CanControl = lambda *_: True
self.core.tracklist.append([
Track(uri='dummy:a'), Track(uri='dummy:b')])
self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')])
self.core.playback.play()
result = self.mpris.Get(objects.PLAYER_IFACE, 'CanGoNext')
self.assertTrue(result)
def test_can_go_next_is_false_if_next_track_is_the_same(self):
self.mpris.get_CanControl = lambda *_: True
self.core.tracklist.append([Track(uri='dummy:a')])
self.core.tracklist.add([Track(uri='dummy:a')])
self.core.playback.repeat = True
self.core.playback.play()
result = self.mpris.Get(objects.PLAYER_IFACE, 'CanGoNext')
@ -286,16 +283,14 @@ class PlayerInterfaceTest(unittest.TestCase):
def test_can_go_next_is_false_if_can_control_is_false(self):
self.mpris.get_CanControl = lambda *_: False
self.core.tracklist.append([
Track(uri='dummy:a'), Track(uri='dummy:b')])
self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')])
self.core.playback.play()
result = self.mpris.Get(objects.PLAYER_IFACE, 'CanGoNext')
self.assertFalse(result)
def test_can_go_previous_is_true_if_can_control_and_previous_track(self):
self.mpris.get_CanControl = lambda *_: True
self.core.tracklist.append([
Track(uri='dummy:a'), Track(uri='dummy:b')])
self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')])
self.core.playback.play()
self.core.playback.next()
result = self.mpris.Get(objects.PLAYER_IFACE, 'CanGoPrevious')
@ -303,7 +298,7 @@ class PlayerInterfaceTest(unittest.TestCase):
def test_can_go_previous_is_false_if_previous_track_is_the_same(self):
self.mpris.get_CanControl = lambda *_: True
self.core.tracklist.append([Track(uri='dummy:a')])
self.core.tracklist.add([Track(uri='dummy:a')])
self.core.playback.repeat = True
self.core.playback.play()
result = self.mpris.Get(objects.PLAYER_IFACE, 'CanGoPrevious')
@ -311,8 +306,7 @@ class PlayerInterfaceTest(unittest.TestCase):
def test_can_go_previous_is_false_if_can_control_is_false(self):
self.mpris.get_CanControl = lambda *_: False
self.core.tracklist.append([
Track(uri='dummy:a'), Track(uri='dummy:b')])
self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')])
self.core.playback.play()
self.core.playback.next()
result = self.mpris.Get(objects.PLAYER_IFACE, 'CanGoPrevious')
@ -320,7 +314,7 @@ class PlayerInterfaceTest(unittest.TestCase):
def test_can_play_is_true_if_can_control_and_current_track(self):
self.mpris.get_CanControl = lambda *_: True
self.core.tracklist.append([Track(uri='dummy:a')])
self.core.tracklist.add([Track(uri='dummy:a')])
self.core.playback.play()
self.assertTrue(self.core.playback.current_track.get())
result = self.mpris.Get(objects.PLAYER_IFACE, 'CanPlay')
@ -363,16 +357,14 @@ class PlayerInterfaceTest(unittest.TestCase):
def test_next_is_ignored_if_can_go_next_is_false(self):
self.mpris.get_CanGoNext = lambda *_: False
self.core.tracklist.append([
Track(uri='dummy:a'), Track(uri='dummy:b')])
self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')])
self.core.playback.play()
self.assertEqual(self.core.playback.current_track.get().uri, 'dummy:a')
self.mpris.Next()
self.assertEqual(self.core.playback.current_track.get().uri, 'dummy:a')
def test_next_when_playing_skips_to_next_track_and_keep_playing(self):
self.core.tracklist.append([
Track(uri='dummy:a'), Track(uri='dummy:b')])
self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')])
self.core.playback.play()
self.assertEqual(self.core.playback.current_track.get().uri, 'dummy:a')
self.assertEqual(self.core.playback.state.get(), PLAYING)
@ -381,8 +373,7 @@ class PlayerInterfaceTest(unittest.TestCase):
self.assertEqual(self.core.playback.state.get(), PLAYING)
def test_next_when_at_end_of_list_should_stop_playback(self):
self.core.tracklist.append([
Track(uri='dummy:a'), Track(uri='dummy:b')])
self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')])
self.core.playback.play()
self.core.playback.next()
self.assertEqual(self.core.playback.current_track.get().uri, 'dummy:b')
@ -391,8 +382,7 @@ class PlayerInterfaceTest(unittest.TestCase):
self.assertEqual(self.core.playback.state.get(), STOPPED)
def test_next_when_paused_should_skip_to_next_track_and_stay_paused(self):
self.core.tracklist.append([
Track(uri='dummy:a'), Track(uri='dummy:b')])
self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')])
self.core.playback.play()
self.core.playback.pause()
self.assertEqual(self.core.playback.current_track.get().uri, 'dummy:a')
@ -402,8 +392,7 @@ class PlayerInterfaceTest(unittest.TestCase):
self.assertEqual(self.core.playback.state.get(), PAUSED)
def test_next_when_stopped_skips_to_next_track_and_stay_stopped(self):
self.core.tracklist.append([
Track(uri='dummy:a'), Track(uri='dummy:b')])
self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')])
self.core.playback.play()
self.core.playback.stop()
self.assertEqual(self.core.playback.current_track.get().uri, 'dummy:a')
@ -414,8 +403,7 @@ class PlayerInterfaceTest(unittest.TestCase):
def test_previous_is_ignored_if_can_go_previous_is_false(self):
self.mpris.get_CanGoPrevious = lambda *_: False
self.core.tracklist.append([
Track(uri='dummy:a'), Track(uri='dummy:b')])
self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')])
self.core.playback.play()
self.core.playback.next()
self.assertEqual(self.core.playback.current_track.get().uri, 'dummy:b')
@ -423,8 +411,7 @@ class PlayerInterfaceTest(unittest.TestCase):
self.assertEqual(self.core.playback.current_track.get().uri, 'dummy:b')
def test_previous_when_playing_skips_to_prev_track_and_keep_playing(self):
self.core.tracklist.append([
Track(uri='dummy:a'), Track(uri='dummy:b')])
self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')])
self.core.playback.play()
self.core.playback.next()
self.assertEqual(self.core.playback.current_track.get().uri, 'dummy:b')
@ -434,8 +421,7 @@ class PlayerInterfaceTest(unittest.TestCase):
self.assertEqual(self.core.playback.state.get(), PLAYING)
def test_previous_when_at_start_of_list_should_stop_playback(self):
self.core.tracklist.append([
Track(uri='dummy:a'), Track(uri='dummy:b')])
self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')])
self.core.playback.play()
self.assertEqual(self.core.playback.current_track.get().uri, 'dummy:a')
self.assertEqual(self.core.playback.state.get(), PLAYING)
@ -443,8 +429,7 @@ class PlayerInterfaceTest(unittest.TestCase):
self.assertEqual(self.core.playback.state.get(), STOPPED)
def test_previous_when_paused_skips_to_previous_track_and_pause(self):
self.core.tracklist.append([
Track(uri='dummy:a'), Track(uri='dummy:b')])
self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')])
self.core.playback.play()
self.core.playback.next()
self.core.playback.pause()
@ -455,8 +440,7 @@ class PlayerInterfaceTest(unittest.TestCase):
self.assertEqual(self.core.playback.state.get(), PAUSED)
def test_previous_when_stopped_skips_to_previous_track_and_stops(self):
self.core.tracklist.append([
Track(uri='dummy:a'), Track(uri='dummy:b')])
self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')])
self.core.playback.play()
self.core.playback.next()
self.core.playback.stop()
@ -468,24 +452,21 @@ class PlayerInterfaceTest(unittest.TestCase):
def test_pause_is_ignored_if_can_pause_is_false(self):
self.mpris.get_CanPause = lambda *_: False
self.core.tracklist.append([
Track(uri='dummy:a'), Track(uri='dummy:b')])
self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')])
self.core.playback.play()
self.assertEqual(self.core.playback.state.get(), PLAYING)
self.mpris.Pause()
self.assertEqual(self.core.playback.state.get(), PLAYING)
def test_pause_when_playing_should_pause_playback(self):
self.core.tracklist.append([
Track(uri='dummy:a'), Track(uri='dummy:b')])
self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')])
self.core.playback.play()
self.assertEqual(self.core.playback.state.get(), PLAYING)
self.mpris.Pause()
self.assertEqual(self.core.playback.state.get(), PAUSED)
def test_pause_when_paused_has_no_effect(self):
self.core.tracklist.append([
Track(uri='dummy:a'), Track(uri='dummy:b')])
self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')])
self.core.playback.play()
self.core.playback.pause()
self.assertEqual(self.core.playback.state.get(), PAUSED)
@ -494,24 +475,21 @@ class PlayerInterfaceTest(unittest.TestCase):
def test_playpause_is_ignored_if_can_pause_is_false(self):
self.mpris.get_CanPause = lambda *_: False
self.core.tracklist.append([
Track(uri='dummy:a'), Track(uri='dummy:b')])
self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')])
self.core.playback.play()
self.assertEqual(self.core.playback.state.get(), PLAYING)
self.mpris.PlayPause()
self.assertEqual(self.core.playback.state.get(), PLAYING)
def test_playpause_when_playing_should_pause_playback(self):
self.core.tracklist.append([
Track(uri='dummy:a'), Track(uri='dummy:b')])
self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')])
self.core.playback.play()
self.assertEqual(self.core.playback.state.get(), PLAYING)
self.mpris.PlayPause()
self.assertEqual(self.core.playback.state.get(), PAUSED)
def test_playpause_when_paused_should_resume_playback(self):
self.core.tracklist.append([
Track(uri='dummy:a'), Track(uri='dummy:b')])
self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')])
self.core.playback.play()
self.core.playback.pause()
@ -526,32 +504,28 @@ class PlayerInterfaceTest(unittest.TestCase):
self.assertGreaterEqual(after_pause, at_pause)
def test_playpause_when_stopped_should_start_playback(self):
self.core.tracklist.append([
Track(uri='dummy:a'), Track(uri='dummy:b')])
self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')])
self.assertEqual(self.core.playback.state.get(), STOPPED)
self.mpris.PlayPause()
self.assertEqual(self.core.playback.state.get(), PLAYING)
def test_stop_is_ignored_if_can_control_is_false(self):
self.mpris.get_CanControl = lambda *_: False
self.core.tracklist.append([
Track(uri='dummy:a'), Track(uri='dummy:b')])
self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')])
self.core.playback.play()
self.assertEqual(self.core.playback.state.get(), PLAYING)
self.mpris.Stop()
self.assertEqual(self.core.playback.state.get(), PLAYING)
def test_stop_when_playing_should_stop_playback(self):
self.core.tracklist.append([
Track(uri='dummy:a'), Track(uri='dummy:b')])
self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')])
self.core.playback.play()
self.assertEqual(self.core.playback.state.get(), PLAYING)
self.mpris.Stop()
self.assertEqual(self.core.playback.state.get(), STOPPED)
def test_stop_when_paused_should_stop_playback(self):
self.core.tracklist.append([
Track(uri='dummy:a'), Track(uri='dummy:b')])
self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')])
self.core.playback.play()
self.core.playback.pause()
self.assertEqual(self.core.playback.state.get(), PAUSED)
@ -560,21 +534,19 @@ class PlayerInterfaceTest(unittest.TestCase):
def test_play_is_ignored_if_can_play_is_false(self):
self.mpris.get_CanPlay = lambda *_: False
self.core.tracklist.append([
Track(uri='dummy:a'), Track(uri='dummy:b')])
self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')])
self.assertEqual(self.core.playback.state.get(), STOPPED)
self.mpris.Play()
self.assertEqual(self.core.playback.state.get(), STOPPED)
def test_play_when_stopped_starts_playback(self):
self.core.tracklist.append([
Track(uri='dummy:a'), Track(uri='dummy:b')])
self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')])
self.assertEqual(self.core.playback.state.get(), STOPPED)
self.mpris.Play()
self.assertEqual(self.core.playback.state.get(), PLAYING)
def test_play_after_pause_resumes_from_same_position(self):
self.core.tracklist.append([Track(uri='dummy:a', length=40000)])
self.core.tracklist.add([Track(uri='dummy:a', length=40000)])
self.core.playback.play()
before_pause = self.core.playback.time_position.get()
@ -598,7 +570,7 @@ class PlayerInterfaceTest(unittest.TestCase):
def test_seek_is_ignored_if_can_seek_is_false(self):
self.mpris.get_CanSeek = lambda *_: False
self.core.tracklist.append([Track(uri='dummy:a', length=40000)])
self.core.tracklist.add([Track(uri='dummy:a', length=40000)])
self.core.playback.play()
before_seek = self.core.playback.time_position.get()
@ -614,7 +586,7 @@ class PlayerInterfaceTest(unittest.TestCase):
self.assertLess(after_seek, before_seek + milliseconds_to_seek)
def test_seek_seeks_given_microseconds_forward_in_the_current_track(self):
self.core.tracklist.append([Track(uri='dummy:a', length=40000)])
self.core.tracklist.add([Track(uri='dummy:a', length=40000)])
self.core.playback.play()
before_seek = self.core.playback.time_position.get()
@ -631,7 +603,7 @@ class PlayerInterfaceTest(unittest.TestCase):
self.assertGreaterEqual(after_seek, before_seek + milliseconds_to_seek)
def test_seek_seeks_given_microseconds_backward_if_negative(self):
self.core.tracklist.append([Track(uri='dummy:a', length=40000)])
self.core.tracklist.add([Track(uri='dummy:a', length=40000)])
self.core.playback.play()
self.core.playback.seek(20000)
@ -650,7 +622,7 @@ class PlayerInterfaceTest(unittest.TestCase):
self.assertLess(after_seek, before_seek)
def test_seek_seeks_to_start_of_track_if_new_position_is_negative(self):
self.core.tracklist.append([Track(uri='dummy:a', length=40000)])
self.core.tracklist.add([Track(uri='dummy:a', length=40000)])
self.core.playback.play()
self.core.playback.seek(20000)
@ -670,7 +642,7 @@ class PlayerInterfaceTest(unittest.TestCase):
self.assertGreaterEqual(after_seek, 0)
def test_seek_skips_to_next_track_if_new_position_gt_track_length(self):
self.core.tracklist.append([
self.core.tracklist.add([
Track(uri='dummy:a', length=40000),
Track(uri='dummy:b')])
self.core.playback.play()
@ -695,7 +667,7 @@ class PlayerInterfaceTest(unittest.TestCase):
def test_set_position_is_ignored_if_can_seek_is_false(self):
self.mpris.get_CanSeek = lambda *_: False
self.core.tracklist.append([Track(uri='dummy:a', length=40000)])
self.core.tracklist.add([Track(uri='dummy:a', length=40000)])
self.core.playback.play()
before_set_position = self.core.playback.time_position.get()
@ -713,7 +685,7 @@ class PlayerInterfaceTest(unittest.TestCase):
self.assertLess(after_set_position, position_to_set_in_millisec)
def test_set_position_sets_the_current_track_position_in_microsecs(self):
self.core.tracklist.append([Track(uri='dummy:a', length=40000)])
self.core.tracklist.add([Track(uri='dummy:a', length=40000)])
self.core.playback.play()
before_set_position = self.core.playback.time_position.get()
@ -734,7 +706,7 @@ class PlayerInterfaceTest(unittest.TestCase):
after_set_position, position_to_set_in_millisec)
def test_set_position_does_nothing_if_the_position_is_negative(self):
self.core.tracklist.append([Track(uri='dummy:a', length=40000)])
self.core.tracklist.add([Track(uri='dummy:a', length=40000)])
self.core.playback.play()
self.core.playback.seek(20000)
@ -757,7 +729,7 @@ class PlayerInterfaceTest(unittest.TestCase):
self.assertEqual(self.core.playback.current_track.get().uri, 'dummy:a')
def test_set_position_does_nothing_if_position_is_gt_track_length(self):
self.core.tracklist.append([Track(uri='dummy:a', length=40000)])
self.core.tracklist.add([Track(uri='dummy:a', length=40000)])
self.core.playback.play()
self.core.playback.seek(20000)
@ -780,7 +752,7 @@ class PlayerInterfaceTest(unittest.TestCase):
self.assertEqual(self.core.playback.current_track.get().uri, 'dummy:a')
def test_set_position_is_noop_if_track_id_isnt_current_track(self):
self.core.tracklist.append([Track(uri='dummy:a', length=40000)])
self.core.tracklist.add([Track(uri='dummy:a', length=40000)])
self.core.playback.play()
self.core.playback.seek(20000)
@ -826,8 +798,7 @@ class PlayerInterfaceTest(unittest.TestCase):
def test_open_uri_starts_playback_of_new_track_if_stopped(self):
self.mpris.get_CanPlay = lambda *_: True
self.backend.library.dummy_library = [Track(uri='dummy:/test/uri')]
self.core.tracklist.append([
Track(uri='dummy:a'), Track(uri='dummy:b')])
self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')])
self.assertEqual(self.core.playback.state.get(), STOPPED)
self.mpris.OpenUri('dummy:/test/uri')
@ -839,8 +810,7 @@ class PlayerInterfaceTest(unittest.TestCase):
def test_open_uri_starts_playback_of_new_track_if_paused(self):
self.mpris.get_CanPlay = lambda *_: True
self.backend.library.dummy_library = [Track(uri='dummy:/test/uri')]
self.core.tracklist.append([
Track(uri='dummy:a'), Track(uri='dummy:b')])
self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')])
self.core.playback.play()
self.core.playback.pause()
self.assertEqual(self.core.playback.state.get(), PAUSED)
@ -855,8 +825,7 @@ class PlayerInterfaceTest(unittest.TestCase):
def test_open_uri_starts_playback_of_new_track_if_playing(self):
self.mpris.get_CanPlay = lambda *_: True
self.backend.library.dummy_library = [Track(uri='dummy:/test/uri')]
self.core.tracklist.append([
Track(uri='dummy:a'), Track(uri='dummy:b')])
self.core.tracklist.add([Track(uri='dummy:a'), Track(uri='dummy:b')])
self.core.playback.play()
self.assertEqual(self.core.playback.state.get(), PLAYING)
self.assertEqual(self.core.playback.current_track.get().uri, 'dummy:a')

View File

@ -44,7 +44,7 @@ class PlayerInterfaceTest(unittest.TestCase):
pykka.ActorRegistry.stop_all()
def test_activate_playlist_appends_tracks_to_tracklist(self):
self.core.tracklist.append([
self.core.tracklist.add([
Track(uri='dummy:old-a'),
Track(uri='dummy:old-b'),
])

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.2'), SV('0.7.3'))
self.assertLess(SV('0.7.3'), SV('0.8.0'))
self.assertLess(SV('0.8.0'), SV(__version__))
self.assertLess(SV(__version__), SV('0.8.2'))
self.assertLess(SV('0.8.0'), SV('0.8.1'))
self.assertLess(SV('0.8.1'), SV(__version__))
self.assertLess(SV(__version__), SV('0.9.1'))