Merge pull request #537 from jodal/tidy-up-core

Core playback and tracklist modularity improvement v2
This commit is contained in:
Thomas Adamcik 2013-10-19 13:24:21 -07:00
commit c8f5c1aacf
11 changed files with 489 additions and 372 deletions

2
.gitignore vendored
View File

@ -14,3 +14,5 @@ docs/_build/
mopidy.log* mopidy.log*
node_modules/ node_modules/
nosetests.xml nosetests.xml
*~
*.orig

View File

@ -39,6 +39,45 @@ of the following extensions as well:
**Core** **Core**
- Parts of the functionality in :class:`mopidy.core.PlaybackController` have
been moved to :class:`mopidy.core.TracklistController`:
=================================== ==================================
Old location New location
=================================== ==================================
playback.get_consume() tracklist.get_consume()
playback.set_consume(v) tracklist.set_consume(v)
playback.consume tracklist.consume
playback.get_random() tracklist.get_random()
playback.set_random(v) tracklist.set_random(v)
playback.random tracklist.random
playback.get_repeat() tracklist.get_repeat()
playback.set_repeat(v) tracklist.set_repeat(v)
playback.repeat tracklist.repeat
playback.get_single() tracklist.get_single()
playback.set_single(v) tracklist.set_single(v)
playback.single tracklist.single
playback.get_tracklist_position() tracklist.index(tl_track)
playback.tracklist_position tracklist.index(tl_track)
playback.get_tl_track_at_eot() tracklist.eot_track(tl_track)
playback.tl_track_at_eot tracklist.eot_track(tl_track)
playback.get_tl_track_at_next() tracklist.next_track(tl_track)
playback.tl_track_at_next tracklist.next_track(tl_track)
playback.get_tl_track_at_previous() tracklist.previous_track(tl_track)
playback.tl_track_at_previous tracklist.previous_track(tl_track)
=================================== ==================================
The ``tl_track`` argument to the last four new functions are used as the
reference ``tl_track`` in the tracklist to find e.g. the next track. Usually,
this will be :attr:`~mopidy.core.PlaybackController.current_tl_track`.
- Added :attr:`mopidy.core.PlaybackController.mute` for muting and unmuting - Added :attr:`mopidy.core.PlaybackController.mute` for muting and unmuting
audio. (Fixes: :issue:`186`) audio. (Fixes: :issue:`186`)

View File

@ -1,7 +1,6 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import logging import logging
import random
import urlparse import urlparse
from mopidy.audio import PlaybackState from mopidy.audio import PlaybackState
@ -21,8 +20,6 @@ class PlaybackController(object):
self.core = core self.core = core
self._state = PlaybackState.STOPPED self._state = PlaybackState.STOPPED
self._shuffled = []
self._first_shuffle = True
self._volume = None self._volume = None
self._mute = False self._mute = False
@ -35,22 +32,6 @@ class PlaybackController(object):
### Properties ### Properties
def get_consume(self):
return getattr(self, '_consume', False)
def set_consume(self, value):
if self.get_consume() != value:
self._trigger_options_changed()
return setattr(self, '_consume', value)
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.
"""
def get_current_tl_track(self): def get_current_tl_track(self):
return self.current_tl_track return self.current_tl_track
@ -70,56 +51,6 @@ class PlaybackController(object):
Read-only. Extracted from :attr:`current_tl_track` for convenience. 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): def get_state(self):
return self._state return self._state
@ -157,119 +88,6 @@ class PlaybackController(object):
time_position = property(get_time_position) time_position = property(get_time_position)
"""Time position in milliseconds.""" """Time position in milliseconds."""
def get_tracklist_position(self):
if self.current_tl_track is None:
return None
try:
return self.core.tracklist.tl_tracks.index(self.current_tl_track)
except ValueError:
return None
tracklist_position = property(get_tracklist_position)
"""
The position of the current track in the tracklist.
Read-only.
"""
def get_tl_track_at_eot(self):
tl_tracks = self.core.tracklist.tl_tracks
if not tl_tracks:
return None
if self.random and not self._shuffled:
if self.repeat or self._first_shuffle:
logger.debug('Shuffling tracks')
self._shuffled = tl_tracks
random.shuffle(self._shuffled)
self._first_shuffle = False
if self.random and self._shuffled:
return self._shuffled[0]
if self.current_tl_track is None:
return tl_tracks[0]
if self.repeat and self.single:
return tl_tracks[self.tracklist_position]
if self.repeat and not self.single:
return tl_tracks[(self.tracklist_position + 1) % len(tl_tracks)]
try:
return tl_tracks[self.tracklist_position + 1]
except IndexError:
return None
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.TlTrack`.
Not necessarily the same track as :attr:`tl_track_at_next`.
"""
def get_tl_track_at_next(self):
tl_tracks = self.core.tracklist.tl_tracks
if not tl_tracks:
return None
if self.random and not self._shuffled:
if self.repeat or self._first_shuffle:
logger.debug('Shuffling tracks')
self._shuffled = tl_tracks
random.shuffle(self._shuffled)
self._first_shuffle = False
if self.random and self._shuffled:
return self._shuffled[0]
if self.current_tl_track is None:
return tl_tracks[0]
if self.repeat:
return tl_tracks[(self.tracklist_position + 1) % len(tl_tracks)]
try:
return tl_tracks[self.tracklist_position + 1]
except IndexError:
return None
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.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_previous(self):
if self.repeat or self.consume or self.random:
return self.current_tl_track
if self.tracklist_position in (None, 0):
return None
return self.core.tracklist.tl_tracks[self.tracklist_position - 1]
tl_track_at_previous = property(get_tl_track_at_previous)
"""
The track that will be played if calling :meth:`previous()`.
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_volume(self): def get_volume(self):
if self.audio: if self.audio:
return self.audio.get_volume().get() return self.audio.get_volume().get()
@ -339,15 +157,15 @@ class PlaybackController(object):
return return
original_tl_track = self.current_tl_track original_tl_track = self.current_tl_track
next_tl_track = self.core.tracklist.eot_track(original_tl_track)
if self.tl_track_at_eot: if next_tl_track:
self._trigger_track_playback_ended() self._trigger_track_playback_ended()
self.play(self.tl_track_at_eot) self.play(next_tl_track)
else: else:
self.stop(clear_current_track=True) self.stop(clear_current_track=True)
if self.consume: self.core.tracklist.mark_played(original_tl_track)
self.core.tracklist.remove(tlid=original_tl_track.tlid)
def on_tracklist_change(self): def on_tracklist_change(self):
""" """
@ -355,12 +173,7 @@ class PlaybackController(object):
Used by :class:`mopidy.core.TracklistController`. Used by :class:`mopidy.core.TracklistController`.
""" """
self._first_shuffle = True if self.current_tl_track not in self.core.tracklist.tl_tracks:
self._shuffled = []
if (not self.core.tracklist.tl_tracks or
self.current_tl_track not in
self.core.tracklist.tl_tracks):
self.stop(clear_current_track=True) self.stop(clear_current_track=True)
def next(self): def next(self):
@ -370,9 +183,10 @@ class PlaybackController(object):
The current playback state will be kept. If it was playing, playing The current playback state will be kept. If it was playing, playing
will continue. If it was paused, it will still be paused, etc. will continue. If it was paused, it will still be paused, etc.
""" """
if self.tl_track_at_next: tl_track = self.core.tracklist.next_track(self.current_tl_track)
if tl_track:
self._trigger_track_playback_ended() self._trigger_track_playback_ended()
self.change_track(self.tl_track_at_next) self.change_track(tl_track)
else: else:
self.stop(clear_current_track=True) self.stop(clear_current_track=True)
@ -395,37 +209,40 @@ class PlaybackController(object):
:type on_error_step: int, -1 or 1 :type on_error_step: int, -1 or 1
""" """
if tl_track is not None: assert on_error_step in (-1, 1)
assert tl_track in self.core.tracklist.tl_tracks
elif tl_track is None: if tl_track is None:
if self.state == PlaybackState.PAUSED: if self.state == PlaybackState.PAUSED:
return self.resume() return self.resume()
elif self.current_tl_track is not None:
tl_track = self.current_tl_track
elif self.current_tl_track is None and on_error_step == 1:
tl_track = self.tl_track_at_next
elif self.current_tl_track is None and on_error_step == -1:
tl_track = self.tl_track_at_previous
if tl_track is not None: if self.current_tl_track is not None:
self.current_tl_track = tl_track tl_track = self.current_tl_track
self.state = PlaybackState.PLAYING else:
backend = self._get_backend()
if not backend or not backend.playback.play(tl_track.track).get():
logger.warning('Track is not playable: %s', tl_track.track.uri)
if self.random and self._shuffled:
self._shuffled.remove(tl_track)
if on_error_step == 1: if on_error_step == 1:
# TODO: can cause an endless loop for single track repeat. tl_track = self.core.tracklist.next_track(tl_track)
self.next()
elif on_error_step == -1: elif on_error_step == -1:
self.previous() tl_track = self.core.tracklist.previous_track(tl_track)
if tl_track is None:
return return
if self.random and self.current_tl_track in self._shuffled: assert tl_track in self.core.tracklist.tl_tracks
self._shuffled.remove(self.current_tl_track)
self._trigger_track_playback_started() self.current_tl_track = tl_track
self.state = PlaybackState.PLAYING
backend = self._get_backend()
success = backend and backend.playback.play(tl_track.track).get()
if success:
self.core.tracklist.mark_playing(tl_track)
self._trigger_track_playback_started()
else:
self.core.tracklist.mark_unplayable(tl_track)
if on_error_step == 1:
# TODO: can cause an endless loop for single track repeat.
self.next()
elif on_error_step == -1:
self.previous()
def previous(self): def previous(self):
""" """
@ -435,7 +252,9 @@ class PlaybackController(object):
will continue. If it was paused, it will still be paused, etc. will continue. If it was paused, it will still be paused, etc.
""" """
self._trigger_track_playback_ended() self._trigger_track_playback_ended()
self.change_track(self.tl_track_at_previous, on_error_step=-1) tl_track = self.current_tl_track
self.change_track(
self.core.tracklist.previous_track(tl_track), on_error_step=-1)
def resume(self): def resume(self):
"""If paused, resume playing the current track.""" """If paused, resume playing the current track."""
@ -531,10 +350,6 @@ class PlaybackController(object):
'playback_state_changed', 'playback_state_changed',
old_state=old_state, new_state=new_state) old_state=old_state, new_state=new_state)
def _trigger_options_changed(self):
logger.debug('Triggering options changed event')
listener.CoreListener.send('options_changed')
def _trigger_volume_changed(self, volume): def _trigger_volume_changed(self, volume):
logger.debug('Triggering volume changed event') logger.debug('Triggering volume changed event')
listener.CoreListener.send('volume_changed', volume=volume) listener.CoreListener.send('volume_changed', volume=volume)

View File

@ -15,11 +15,16 @@ class TracklistController(object):
pykka_traversable = True pykka_traversable = True
def __init__(self, core): def __init__(self, core):
self._core = core self.core = core
self._next_tlid = 0 self._next_tlid = 0
self._tl_tracks = [] self._tl_tracks = []
self._version = 0 self._version = 0
self._shuffled = []
self._first_shuffle = True
### Properties
def get_tl_tracks(self): def get_tl_tracks(self):
return self._tl_tracks[:] return self._tl_tracks[:]
@ -51,7 +56,7 @@ class TracklistController(object):
def _increase_version(self): def _increase_version(self):
self._version += 1 self._version += 1
self._core.playback.on_tracklist_change() self.core.playback.on_tracklist_change()
self._trigger_tracklist_changed() self._trigger_tracklist_changed()
version = property(get_version) version = property(get_version)
@ -62,6 +67,190 @@ class TracklistController(object):
Is not reset before Mopidy is restarted. Is not reset before Mopidy is restarted.
""" """
def get_consume(self):
return getattr(self, '_consume', False)
def set_consume(self, value):
if self.get_consume() != value:
self._trigger_options_changed()
return setattr(self, '_consume', value)
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.
"""
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.
"""
### Methods
def index(self, tl_track):
"""
The position of the given track in the tracklist.
:param tl_track: the track to find the index of
:type tl_track: :class:`mopidy.models.TlTrack`
:rtype: :class:`int` or :class:`None`
"""
try:
return self._tl_tracks.index(tl_track)
except ValueError:
return None
def eot_track(self, tl_track):
"""
The track that will be played after the given track.
Not necessarily the same track as :meth:`next_track`.
:param tl_track: the reference track
:type tl_track: :class:`mopidy.models.TlTrack` or :class:`None`
:rtype: :class:`mopidy.models.TlTrack` or :class:`None`
"""
if not self.tl_tracks:
return None
if self.random and not self._shuffled:
if self.repeat or self._first_shuffle:
logger.debug('Shuffling tracks')
self._shuffled = self.tl_tracks
random.shuffle(self._shuffled)
self._first_shuffle = False
if self.random and self._shuffled:
return self._shuffled[0]
if tl_track is None:
return self.tl_tracks[0]
position = self.index(tl_track)
if self.repeat and self.single:
return self.tl_tracks[position]
if self.repeat and not self.single:
return self.tl_tracks[(position + 1) % len(self.tl_tracks)]
try:
return self.tl_tracks[position + 1]
except IndexError:
return None
def next_track(self, tl_track):
"""
The track that will be played if calling
:meth:`mopidy.core.PlaybackController.next()`.
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.
:param tl_track: the reference track
:type tl_track: :class:`mopidy.models.TlTrack` or :class:`None`
:rtype: :class:`mopidy.models.TlTrack` or :class:`None`
"""
if not self.tl_tracks:
return None
if self.random and not self._shuffled:
if self.repeat or self._first_shuffle:
logger.debug('Shuffling tracks')
self._shuffled = self.tl_tracks
random.shuffle(self._shuffled)
self._first_shuffle = False
if self.random and self._shuffled:
return self._shuffled[0]
if tl_track is None:
return self.tl_tracks[0]
position = self.index(tl_track)
if self.repeat:
return self.tl_tracks[(position + 1) % len(self.tl_tracks)]
try:
return self.tl_tracks[position + 1]
except IndexError:
return None
def previous_track(self, tl_track):
"""
Returns the track that will be played if calling
:meth:`mopidy.core.PlaybackController.previous()`.
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.
:param tl_track: the reference track
:type tl_track: :class:`mopidy.models.TlTrack` or :class:`None`
:rtype: :class:`mopidy.models.TlTrack` or :class:`None`
"""
if self.repeat or self.consume or self.random:
return tl_track
position = self.index(tl_track)
if position in (None, 0):
return None
return self.tl_tracks[position - 1]
def add(self, tracks=None, at_position=None, uri=None): def add(self, tracks=None, at_position=None, uri=None):
""" """
Add the track or list of tracks to the tracklist. Add the track or list of tracks to the tracklist.
@ -87,7 +276,7 @@ class TracklistController(object):
'tracks or uri must be provided' 'tracks or uri must be provided'
if tracks is None and uri is not None: if tracks is None and uri is not None:
tracks = self._core.library.lookup(uri) tracks = self.core.library.lookup(uri)
tl_tracks = [] tl_tracks = []
@ -151,18 +340,6 @@ class TracklistController(object):
lambda ct: getattr(ct.track, key) == value, matches) lambda ct: getattr(ct.track, key) == value, matches)
return matches return matches
def index(self, tl_track):
"""
Get index of the given :class:`mopidy.models.TlTrack` in the tracklist.
Raises :exc:`ValueError` if not found.
:param tl_track: track to find the index of
:type tl_track: :class:`mopidy.models.TlTrack`
:rtype: int
"""
return self._tl_tracks.index(tl_track)
def move(self, start, end, to_position): def move(self, start, end, to_position):
""" """
Move the tracks in the slice ``[start:end]`` to ``to_position``. Move the tracks in the slice ``[start:end]`` to ``to_position``.
@ -259,6 +436,31 @@ class TracklistController(object):
""" """
return self._tl_tracks[start:end] return self._tl_tracks[start:end]
def mark_playing(self, tl_track):
"""Private method used by :class:`mopidy.core.PlaybackController`."""
if self.random and tl_track in self._shuffled:
self._shuffled.remove(tl_track)
def mark_unplayable(self, tl_track):
"""Private method used by :class:`mopidy.core.PlaybackController`."""
logger.warning('Track is not playable: %s', tl_track.track.uri)
if self.random and tl_track in self._shuffled:
self._shuffled.remove(tl_track)
def mark_played(self, tl_track):
"""Private method used by :class:`mopidy.core.PlaybackController`."""
if not self.consume:
return False
self.remove(tlid=tl_track.tlid)
return True
def _trigger_tracklist_changed(self): def _trigger_tracklist_changed(self):
self._first_shuffle = True
self._shuffled = []
logger.debug('Triggering event: tracklist_changed()') logger.debug('Triggering event: tracklist_changed()')
listener.CoreListener.send('tracklist_changed') listener.CoreListener.send('tracklist_changed')
def _trigger_options_changed(self):
logger.debug('Triggering options changed event')
listener.CoreListener.send('options_changed')

View File

@ -19,9 +19,9 @@ def consume(context, state):
playlist. playlist.
""" """
if int(state): if int(state):
context.core.playback.consume = True context.core.tracklist.consume = True
else: else:
context.core.playback.consume = False context.core.tracklist.consume = False
@handle_request(r'^crossfade "(?P<seconds>\d+)"$') @handle_request(r'^crossfade "(?P<seconds>\d+)"$')
@ -263,9 +263,9 @@ def random(context, state):
Sets random state to ``STATE``, ``STATE`` should be 0 or 1. Sets random state to ``STATE``, ``STATE`` should be 0 or 1.
""" """
if int(state): if int(state):
context.core.playback.random = True context.core.tracklist.random = True
else: else:
context.core.playback.random = False context.core.tracklist.random = False
@handle_request(r'^repeat (?P<state>[01])$') @handle_request(r'^repeat (?P<state>[01])$')
@ -279,9 +279,9 @@ def repeat(context, state):
Sets repeat state to ``STATE``, ``STATE`` should be 0 or 1. Sets repeat state to ``STATE``, ``STATE`` should be 0 or 1.
""" """
if int(state): if int(state):
context.core.playback.repeat = True context.core.tracklist.repeat = True
else: else:
context.core.playback.repeat = False context.core.tracklist.repeat = False
@handle_request(r'^replay_gain_mode "(?P<mode>(off|track|album))"$') @handle_request(r'^replay_gain_mode "(?P<mode>(off|track|album))"$')
@ -329,7 +329,8 @@ def seek(context, songpos, seconds):
- issues ``seek 1 120`` without quotes around the arguments. - issues ``seek 1 120`` without quotes around the arguments.
""" """
if context.core.playback.tracklist_position.get() != int(songpos): tl_track = context.core.playback.current_tl_track.get()
if context.core.tracklist.index(tl_track).get() != int(songpos):
playpos(context, songpos) playpos(context, songpos)
context.core.playback.seek(int(seconds) * 1000).get() context.core.playback.seek(int(seconds) * 1000).get()
@ -404,9 +405,9 @@ def single(context, state):
song is repeated if the ``repeat`` mode is enabled. song is repeated if the ``repeat`` mode is enabled.
""" """
if int(state): if int(state):
context.core.playback.single = True context.core.tracklist.single = True
else: else:
context.core.playback.single = False context.core.tracklist.single = False
@handle_request(r'^stop$') @handle_request(r'^stop$')

View File

@ -36,10 +36,10 @@ def currentsong(context):
Displays the song info of the current song (same song that is Displays the song info of the current song (same song that is
identified in status). identified in status).
""" """
current_tl_track = context.core.playback.current_tl_track.get() tl_track = context.core.playback.current_tl_track.get()
if current_tl_track is not None: if tl_track is not None:
position = context.core.playback.tracklist_position.get() position = context.core.tracklist.index(tl_track).get()
return track_to_mpd_format(current_tl_track, position=position) return track_to_mpd_format(tl_track, position=position)
@handle_request(r'^idle$') @handle_request(r'^idle$')
@ -178,14 +178,15 @@ def status(context):
'tracklist.length': context.core.tracklist.length, 'tracklist.length': context.core.tracklist.length,
'tracklist.version': context.core.tracklist.version, 'tracklist.version': context.core.tracklist.version,
'playback.volume': context.core.playback.volume, 'playback.volume': context.core.playback.volume,
'playback.consume': context.core.playback.consume, 'tracklist.consume': context.core.tracklist.consume,
'playback.random': context.core.playback.random, 'tracklist.random': context.core.tracklist.random,
'playback.repeat': context.core.playback.repeat, 'tracklist.repeat': context.core.tracklist.repeat,
'playback.single': context.core.playback.single, 'tracklist.single': context.core.tracklist.single,
'playback.state': context.core.playback.state, 'playback.state': context.core.playback.state,
'playback.current_tl_track': context.core.playback.current_tl_track, 'playback.current_tl_track': context.core.playback.current_tl_track,
'playback.tracklist_position': ( 'tracklist.index': (
context.core.playback.tracklist_position), context.core.tracklist.index(
context.core.playback.current_tl_track.get())),
'playback.time_position': context.core.playback.time_position, 'playback.time_position': context.core.playback.time_position,
} }
pykka.get_all(futures.values()) pykka.get_all(futures.values())
@ -218,7 +219,7 @@ def _status_bitrate(futures):
def _status_consume(futures): def _status_consume(futures):
if futures['playback.consume'].get(): if futures['tracklist.consume'].get():
return 1 return 1
else: else:
return 0 return 0
@ -233,15 +234,15 @@ def _status_playlist_version(futures):
def _status_random(futures): def _status_random(futures):
return int(futures['playback.random'].get()) return int(futures['tracklist.random'].get())
def _status_repeat(futures): def _status_repeat(futures):
return int(futures['playback.repeat'].get()) return int(futures['tracklist.repeat'].get())
def _status_single(futures): def _status_single(futures):
return int(futures['playback.single'].get()) return int(futures['tracklist.single'].get())
def _status_songid(futures): def _status_songid(futures):
@ -253,7 +254,7 @@ def _status_songid(futures):
def _status_songpos(futures): def _status_songpos(futures):
return futures['playback.tracklist_position'].get() return futures['tracklist.index'].get()
def _status_state(futures): def _status_state(futures):

View File

@ -212,13 +212,15 @@ class LocalPlaybackProviderTest(unittest.TestCase):
def test_next(self): def test_next(self):
self.playback.play() self.playback.play()
old_position = self.playback.tracklist_position tl_track = self.playback.current_tl_track
old_uri = self.playback.current_track.uri old_position = self.tracklist.index(tl_track)
old_uri = tl_track.track.uri
self.playback.next() self.playback.next()
tl_track = self.playback.current_tl_track
self.assertEqual( self.assertEqual(
self.playback.tracklist_position, old_position + 1) self.tracklist.index(tl_track), old_position + 1)
self.assertNotEqual(self.playback.current_track.uri, old_uri) self.assertNotEqual(self.playback.current_track.uri, old_uri)
@populate_tracklist @populate_tracklist
@ -238,7 +240,8 @@ class LocalPlaybackProviderTest(unittest.TestCase):
for i, track in enumerate(self.tracks): for i, track in enumerate(self.tracks):
self.assertEqual(self.playback.state, PlaybackState.PLAYING) self.assertEqual(self.playback.state, PlaybackState.PLAYING)
self.assertEqual(self.playback.current_track, track) self.assertEqual(self.playback.current_track, track)
self.assertEqual(self.playback.tracklist_position, i) tl_track = self.playback.current_tl_track
self.assertEqual(self.tracklist.index(tl_track), i)
self.playback.next() self.playback.next()
@ -274,55 +277,67 @@ class LocalPlaybackProviderTest(unittest.TestCase):
@populate_tracklist @populate_tracklist
def test_next_track_before_play(self): def test_next_track_before_play(self):
self.assertEqual(self.playback.tl_track_at_next, self.tl_tracks[0]) tl_track = self.playback.current_tl_track
self.assertEqual(
self.tracklist.next_track(tl_track), self.tl_tracks[0])
@populate_tracklist @populate_tracklist
def test_next_track_during_play(self): def test_next_track_during_play(self):
self.playback.play() self.playback.play()
self.assertEqual(self.playback.tl_track_at_next, self.tl_tracks[1]) tl_track = self.playback.current_tl_track
self.assertEqual(
self.tracklist.next_track(tl_track), self.tl_tracks[1])
@populate_tracklist @populate_tracklist
def test_next_track_after_previous(self): def test_next_track_after_previous(self):
self.playback.play() self.playback.play()
self.playback.next() self.playback.next()
self.playback.previous() self.playback.previous()
self.assertEqual(self.playback.tl_track_at_next, self.tl_tracks[1]) tl_track = self.playback.current_tl_track
self.assertEqual(
self.tracklist.next_track(tl_track), self.tl_tracks[1])
def test_next_track_empty_playlist(self): def test_next_track_empty_playlist(self):
self.assertEqual(self.playback.tl_track_at_next, None) tl_track = self.playback.current_tl_track
self.assertEqual(self.tracklist.next_track(tl_track), None)
@populate_tracklist @populate_tracklist
def test_next_track_at_end_of_playlist(self): def test_next_track_at_end_of_playlist(self):
self.playback.play() self.playback.play()
for _ in self.tracklist.tl_tracks[1:]: for _ in self.tracklist.tl_tracks[1:]:
self.playback.next() self.playback.next()
self.assertEqual(self.playback.tl_track_at_next, None) tl_track = self.playback.current_tl_track
self.assertEqual(self.tracklist.next_track(tl_track), None)
@populate_tracklist @populate_tracklist
def test_next_track_at_end_of_playlist_with_repeat(self): def test_next_track_at_end_of_playlist_with_repeat(self):
self.playback.repeat = True self.tracklist.repeat = True
self.playback.play() self.playback.play()
for _ in self.tracks[1:]: for _ in self.tracks[1:]:
self.playback.next() self.playback.next()
self.assertEqual(self.playback.tl_track_at_next, self.tl_tracks[0]) tl_track = self.playback.current_tl_track
self.assertEqual(
self.tracklist.next_track(tl_track), self.tl_tracks[0])
@populate_tracklist @populate_tracklist
def test_next_track_with_random(self): def test_next_track_with_random(self):
random.seed(1) random.seed(1)
self.playback.random = True self.tracklist.random = True
self.assertEqual(self.playback.tl_track_at_next, self.tl_tracks[2]) tl_track = self.playback.current_tl_track
self.assertEqual(
self.tracklist.next_track(tl_track), self.tl_tracks[2])
@populate_tracklist @populate_tracklist
def test_next_with_consume(self): def test_next_with_consume(self):
self.playback.consume = True self.tracklist.consume = True
self.playback.play() self.playback.play()
self.playback.next() self.playback.next()
self.assertIn(self.tracks[0], self.tracklist.tracks) self.assertIn(self.tracks[0], self.tracklist.tracks)
@populate_tracklist @populate_tracklist
def test_next_with_single_and_repeat(self): def test_next_with_single_and_repeat(self):
self.playback.single = True self.tracklist.single = True
self.playback.repeat = True self.tracklist.repeat = True
self.playback.play() self.playback.play()
self.playback.next() self.playback.next()
self.assertEqual(self.playback.current_track, self.tracks[1]) self.assertEqual(self.playback.current_track, self.tracks[1])
@ -331,7 +346,7 @@ class LocalPlaybackProviderTest(unittest.TestCase):
def test_next_with_random(self): def test_next_with_random(self):
# FIXME feels very fragile # FIXME feels very fragile
random.seed(1) random.seed(1)
self.playback.random = True self.tracklist.random = True
self.playback.play() self.playback.play()
self.playback.next() self.playback.next()
self.assertEqual(self.playback.current_track, self.tracks[1]) self.assertEqual(self.playback.current_track, self.tracks[1])
@ -339,22 +354,28 @@ class LocalPlaybackProviderTest(unittest.TestCase):
@populate_tracklist @populate_tracklist
def test_next_track_with_random_after_append_playlist(self): def test_next_track_with_random_after_append_playlist(self):
random.seed(1) random.seed(1)
self.playback.random = True self.tracklist.random = True
self.assertEqual(self.playback.tl_track_at_next, self.tl_tracks[2]) tl_track = self.playback.current_tl_track
self.assertEqual(self.tracklist.next_track(tl_track),
self.tl_tracks[2])
self.tracklist.add(self.tracks[:1]) self.tracklist.add(self.tracks[:1])
self.assertEqual(self.playback.tl_track_at_next, self.tl_tracks[1]) tl_track = self.playback.current_tl_track
self.assertEqual(
self.tracklist.next_track(tl_track), self.tl_tracks[1])
@populate_tracklist @populate_tracklist
def test_end_of_track(self): def test_end_of_track(self):
self.playback.play() self.playback.play()
old_position = self.playback.tracklist_position tl_track = self.playback.current_tl_track
old_uri = self.playback.current_track.uri old_position = self.tracklist.index(tl_track)
old_uri = tl_track.track.uri
self.playback.on_end_of_track() self.playback.on_end_of_track()
tl_track = self.playback.current_tl_track
self.assertEqual( self.assertEqual(
self.playback.tracklist_position, old_position + 1) self.tracklist.index(tl_track), old_position + 1)
self.assertNotEqual(self.playback.current_track.uri, old_uri) self.assertNotEqual(self.playback.current_track.uri, old_uri)
@populate_tracklist @populate_tracklist
@ -374,7 +395,8 @@ class LocalPlaybackProviderTest(unittest.TestCase):
for i, track in enumerate(self.tracks): for i, track in enumerate(self.tracks):
self.assertEqual(self.playback.state, PlaybackState.PLAYING) self.assertEqual(self.playback.state, PlaybackState.PLAYING)
self.assertEqual(self.playback.current_track, track) self.assertEqual(self.playback.current_track, track)
self.assertEqual(self.playback.tracklist_position, i) tl_track = self.playback.current_tl_track
self.assertEqual(self.tracklist.index(tl_track), i)
self.playback.on_end_of_track() self.playback.on_end_of_track()
@ -410,47 +432,59 @@ class LocalPlaybackProviderTest(unittest.TestCase):
@populate_tracklist @populate_tracklist
def test_end_of_track_track_before_play(self): def test_end_of_track_track_before_play(self):
self.assertEqual(self.playback.tl_track_at_next, self.tl_tracks[0]) tl_track = self.playback.current_tl_track
self.assertEqual(
self.tracklist.next_track(tl_track), self.tl_tracks[0])
@populate_tracklist @populate_tracklist
def test_end_of_track_track_during_play(self): def test_end_of_track_track_during_play(self):
self.playback.play() self.playback.play()
self.assertEqual(self.playback.tl_track_at_next, self.tl_tracks[1]) tl_track = self.playback.current_tl_track
self.assertEqual(
self.tracklist.next_track(tl_track), self.tl_tracks[1])
@populate_tracklist @populate_tracklist
def test_end_of_track_track_after_previous(self): def test_end_of_track_track_after_previous(self):
self.playback.play() self.playback.play()
self.playback.on_end_of_track() self.playback.on_end_of_track()
self.playback.previous() self.playback.previous()
self.assertEqual(self.playback.tl_track_at_next, self.tl_tracks[1]) tl_track = self.playback.current_tl_track
self.assertEqual(
self.tracklist.next_track(tl_track), self.tl_tracks[1])
def test_end_of_track_track_empty_playlist(self): def test_end_of_track_track_empty_playlist(self):
self.assertEqual(self.playback.tl_track_at_next, None) tl_track = self.playback.current_tl_track
self.assertEqual(self.tracklist.next_track(tl_track), None)
@populate_tracklist @populate_tracklist
def test_end_of_track_track_at_end_of_playlist(self): def test_end_of_track_track_at_end_of_playlist(self):
self.playback.play() self.playback.play()
for _ in self.tracklist.tl_tracks[1:]: for _ in self.tracklist.tl_tracks[1:]:
self.playback.on_end_of_track() self.playback.on_end_of_track()
self.assertEqual(self.playback.tl_track_at_next, None) tl_track = self.playback.current_tl_track
self.assertEqual(self.tracklist.next_track(tl_track), None)
@populate_tracklist @populate_tracklist
def test_end_of_track_track_at_end_of_playlist_with_repeat(self): def test_end_of_track_track_at_end_of_playlist_with_repeat(self):
self.playback.repeat = True self.tracklist.repeat = True
self.playback.play() self.playback.play()
for _ in self.tracks[1:]: for _ in self.tracks[1:]:
self.playback.on_end_of_track() self.playback.on_end_of_track()
self.assertEqual(self.playback.tl_track_at_next, self.tl_tracks[0]) tl_track = self.playback.current_tl_track
self.assertEqual(
self.tracklist.next_track(tl_track), self.tl_tracks[0])
@populate_tracklist @populate_tracklist
def test_end_of_track_track_with_random(self): def test_end_of_track_track_with_random(self):
random.seed(1) random.seed(1)
self.playback.random = True self.tracklist.random = True
self.assertEqual(self.playback.tl_track_at_next, self.tl_tracks[2]) tl_track = self.playback.current_tl_track
self.assertEqual(
self.tracklist.next_track(tl_track), self.tl_tracks[2])
@populate_tracklist @populate_tracklist
def test_end_of_track_with_consume(self): def test_end_of_track_with_consume(self):
self.playback.consume = True self.tracklist.consume = True
self.playback.play() self.playback.play()
self.playback.on_end_of_track() self.playback.on_end_of_track()
self.assertNotIn(self.tracks[0], self.tracklist.tracks) self.assertNotIn(self.tracks[0], self.tracklist.tracks)
@ -459,7 +493,7 @@ class LocalPlaybackProviderTest(unittest.TestCase):
def test_end_of_track_with_random(self): def test_end_of_track_with_random(self):
# FIXME feels very fragile # FIXME feels very fragile
random.seed(1) random.seed(1)
self.playback.random = True self.tracklist.random = True
self.playback.play() self.playback.play()
self.playback.on_end_of_track() self.playback.on_end_of_track()
self.assertEqual(self.playback.current_track, self.tracks[1]) self.assertEqual(self.playback.current_track, self.tracks[1])
@ -467,25 +501,33 @@ class LocalPlaybackProviderTest(unittest.TestCase):
@populate_tracklist @populate_tracklist
def test_end_of_track_track_with_random_after_append_playlist(self): def test_end_of_track_track_with_random_after_append_playlist(self):
random.seed(1) random.seed(1)
self.playback.random = True self.tracklist.random = True
self.assertEqual(self.playback.tl_track_at_next, self.tl_tracks[2]) tl_track = self.playback.current_tl_track
self.assertEqual(
self.tracklist.next_track(tl_track), self.tl_tracks[2])
self.tracklist.add(self.tracks[:1]) self.tracklist.add(self.tracks[:1])
self.assertEqual(self.playback.tl_track_at_next, self.tl_tracks[1]) tl_track = self.playback.current_tl_track
self.assertEqual(
self.tracklist.next_track(tl_track), self.tl_tracks[1])
@populate_tracklist @populate_tracklist
def test_previous_track_before_play(self): def test_previous_track_before_play(self):
self.assertEqual(self.playback.tl_track_at_previous, None) tl_track = self.playback.current_tl_track
self.assertEqual(self.tracklist.previous_track(tl_track), None)
@populate_tracklist @populate_tracklist
def test_previous_track_after_play(self): def test_previous_track_after_play(self):
self.playback.play() self.playback.play()
self.assertEqual(self.playback.tl_track_at_previous, None) tl_track = self.playback.current_tl_track
self.assertEqual(self.tracklist.previous_track(tl_track), None)
@populate_tracklist @populate_tracklist
def test_previous_track_after_next(self): def test_previous_track_after_next(self):
self.playback.play() self.playback.play()
self.playback.next() self.playback.next()
self.assertEqual(self.playback.tl_track_at_previous, self.tl_tracks[0]) tl_track = self.playback.current_tl_track
self.assertEqual(
self.tracklist.previous_track(tl_track), self.tl_tracks[0])
@populate_tracklist @populate_tracklist
def test_previous_track_after_previous(self): def test_previous_track_after_previous(self):
@ -493,27 +535,32 @@ class LocalPlaybackProviderTest(unittest.TestCase):
self.playback.next() # At track 1 self.playback.next() # At track 1
self.playback.next() # At track 2 self.playback.next() # At track 2
self.playback.previous() # At track 1 self.playback.previous() # At track 1
self.assertEqual(self.playback.tl_track_at_previous, self.tl_tracks[0]) tl_track = self.playback.current_tl_track
self.assertEqual(
self.tracklist.previous_track(tl_track), self.tl_tracks[0])
def test_previous_track_empty_playlist(self): def test_previous_track_empty_playlist(self):
self.assertEqual(self.playback.tl_track_at_previous, None) tl_track = self.playback.current_tl_track
self.assertEqual(self.tracklist.previous_track(tl_track), None)
@populate_tracklist @populate_tracklist
def test_previous_track_with_consume(self): def test_previous_track_with_consume(self):
self.playback.consume = True self.tracklist.consume = True
for _ in self.tracks: for _ in self.tracks:
self.playback.next() self.playback.next()
tl_track = self.playback.current_tl_track
self.assertEqual( self.assertEqual(
self.playback.tl_track_at_previous, self.tracklist.previous_track(tl_track),
self.playback.current_tl_track) self.playback.current_tl_track)
@populate_tracklist @populate_tracklist
def test_previous_track_with_random(self): def test_previous_track_with_random(self):
self.playback.random = True self.tracklist.random = True
for _ in self.tracks: for _ in self.tracks:
self.playback.next() self.playback.next()
tl_track = self.playback.current_tl_track
self.assertEqual( self.assertEqual(
self.playback.tl_track_at_previous, self.tracklist.previous_track(tl_track),
self.playback.current_tl_track) self.playback.current_tl_track)
@populate_tracklist @populate_tracklist
@ -533,24 +580,28 @@ class LocalPlaybackProviderTest(unittest.TestCase):
@populate_tracklist @populate_tracklist
def test_initial_tracklist_position(self): def test_initial_tracklist_position(self):
self.assertEqual(self.playback.tracklist_position, None) tl_track = self.playback.current_tl_track
self.assertEqual(self.tracklist.index(tl_track), None)
@populate_tracklist @populate_tracklist
def test_tracklist_position_during_play(self): def test_tracklist_position_during_play(self):
self.playback.play() self.playback.play()
self.assertEqual(self.playback.tracklist_position, 0) tl_track = self.playback.current_tl_track
self.assertEqual(self.tracklist.index(tl_track), 0)
@populate_tracklist @populate_tracklist
def test_tracklist_position_after_next(self): def test_tracklist_position_after_next(self):
self.playback.play() self.playback.play()
self.playback.next() self.playback.next()
self.assertEqual(self.playback.tracklist_position, 1) tl_track = self.playback.current_tl_track
self.assertEqual(self.tracklist.index(tl_track), 1)
@populate_tracklist @populate_tracklist
def test_tracklist_position_at_end_of_playlist(self): def test_tracklist_position_at_end_of_playlist(self):
self.playback.play(self.tracklist.tl_tracks[-1]) self.playback.play(self.tracklist.tl_tracks[-1])
self.playback.on_end_of_track() self.playback.on_end_of_track()
self.assertEqual(self.playback.tracklist_position, None) tl_track = self.playback.current_tl_track
self.assertEqual(self.tracklist.index(tl_track), None)
def test_on_tracklist_change_gets_called(self): def test_on_tracklist_change_gets_called(self):
callback = self.playback.on_tracklist_change callback = self.playback.on_tracklist_change
@ -808,13 +859,13 @@ class LocalPlaybackProviderTest(unittest.TestCase):
@populate_tracklist @populate_tracklist
def test_play_with_consume(self): def test_play_with_consume(self):
self.playback.consume = True self.tracklist.consume = True
self.playback.play() self.playback.play()
self.assertEqual(self.playback.current_track, self.tracks[0]) self.assertEqual(self.playback.current_track, self.tracks[0])
@populate_tracklist @populate_tracklist
def test_playlist_is_empty_after_all_tracks_are_played_with_consume(self): def test_playlist_is_empty_after_all_tracks_are_played_with_consume(self):
self.playback.consume = True self.tracklist.consume = True
self.playback.play() self.playback.play()
for _ in range(len(self.tracklist.tracks)): for _ in range(len(self.tracklist.tracks)):
self.playback.on_end_of_track() self.playback.on_end_of_track()
@ -823,14 +874,14 @@ class LocalPlaybackProviderTest(unittest.TestCase):
@populate_tracklist @populate_tracklist
def test_play_with_random(self): def test_play_with_random(self):
random.seed(1) random.seed(1)
self.playback.random = True self.tracklist.random = True
self.playback.play() self.playback.play()
self.assertEqual(self.playback.current_track, self.tracks[2]) self.assertEqual(self.playback.current_track, self.tracks[2])
@populate_tracklist @populate_tracklist
def test_previous_with_random(self): def test_previous_with_random(self):
random.seed(1) random.seed(1)
self.playback.random = True self.tracklist.random = True
self.playback.play() self.playback.play()
self.playback.next() self.playback.next()
current_track = self.playback.current_track current_track = self.playback.current_track
@ -845,8 +896,8 @@ class LocalPlaybackProviderTest(unittest.TestCase):
@populate_tracklist @populate_tracklist
def test_end_of_song_with_single_and_repeat_starts_same(self): def test_end_of_song_with_single_and_repeat_starts_same(self):
self.playback.single = True self.tracklist.single = True
self.playback.repeat = True self.tracklist.repeat = True
self.playback.play() self.playback.play()
self.playback.on_end_of_track() self.playback.on_end_of_track()
self.assertEqual(self.playback.current_track, self.tracks[0]) self.assertEqual(self.playback.current_track, self.tracks[0])
@ -858,44 +909,47 @@ class LocalPlaybackProviderTest(unittest.TestCase):
self.assertEqual(self.playback.state, PlaybackState.STOPPED) self.assertEqual(self.playback.state, PlaybackState.STOPPED)
def test_repeat_off_by_default(self): def test_repeat_off_by_default(self):
self.assertEqual(self.playback.repeat, False) self.assertEqual(self.tracklist.repeat, False)
def test_random_off_by_default(self): def test_random_off_by_default(self):
self.assertEqual(self.playback.random, False) self.assertEqual(self.tracklist.random, False)
def test_consume_off_by_default(self): def test_consume_off_by_default(self):
self.assertEqual(self.playback.consume, False) self.assertEqual(self.tracklist.consume, False)
@populate_tracklist @populate_tracklist
def test_random_until_end_of_playlist(self): def test_random_until_end_of_playlist(self):
self.playback.random = True self.tracklist.random = True
self.playback.play() self.playback.play()
for _ in self.tracks[1:]: for _ in self.tracks[1:]:
self.playback.next() self.playback.next()
self.assertEqual(self.playback.tl_track_at_next, None) tl_track = self.playback.current_tl_track
self.assertEqual(self.tracklist.next_track(tl_track), None)
@populate_tracklist @populate_tracklist
def test_random_until_end_of_playlist_and_play_from_start(self): def test_random_until_end_of_playlist_and_play_from_start(self):
self.playback.repeat = True self.tracklist.repeat = True
for _ in self.tracks: for _ in self.tracks:
self.playback.next() self.playback.next()
self.assertNotEqual(self.playback.tl_track_at_next, None) tl_track = self.playback.current_tl_track
self.assertNotEqual(self.tracklist.next_track(tl_track), None)
self.assertEqual(self.playback.state, PlaybackState.STOPPED) self.assertEqual(self.playback.state, PlaybackState.STOPPED)
self.playback.play() self.playback.play()
self.assertEqual(self.playback.state, PlaybackState.PLAYING) self.assertEqual(self.playback.state, PlaybackState.PLAYING)
@populate_tracklist @populate_tracklist
def test_random_until_end_of_playlist_with_repeat(self): def test_random_until_end_of_playlist_with_repeat(self):
self.playback.repeat = True self.tracklist.repeat = True
self.playback.random = True self.tracklist.random = True
self.playback.play() self.playback.play()
for _ in self.tracks: for _ in self.tracks:
self.playback.next() self.playback.next()
self.assertNotEqual(self.playback.tl_track_at_next, None) tl_track = self.playback.current_tl_track
self.assertNotEqual(self.tracklist.next_track(tl_track), None)
@populate_tracklist @populate_tracklist
def test_played_track_during_random_not_played_again(self): def test_played_track_during_random_not_played_again(self):
self.playback.random = True self.tracklist.random = True
self.playback.play() self.playback.play()
played = [] played = []
for _ in self.tracks: for _ in self.tracks:

View File

@ -171,13 +171,13 @@ class LocalTracklistProviderTest(unittest.TestCase):
def test_index_returns_index_of_track(self): def test_index_returns_index_of_track(self):
tl_tracks = self.controller.add(self.tracks) tl_tracks = self.controller.add(self.tracks)
self.assertEquals(0, self.controller.index(tl_tracks[0])) self.assertEqual(0, self.controller.index(tl_tracks[0]))
self.assertEquals(1, self.controller.index(tl_tracks[1])) self.assertEqual(1, self.controller.index(tl_tracks[1]))
self.assertEquals(2, self.controller.index(tl_tracks[2])) self.assertEqual(2, self.controller.index(tl_tracks[2]))
def test_index_raises_value_error_if_item_not_found(self): def test_index_returns_none_if_item_not_found(self):
test = lambda: self.controller.index(TlTrack(0, Track())) tl_track = TlTrack(0, Track())
self.assertRaises(ValueError, test) self.assertEqual(self.controller.index(tl_track), None)
@populate_tracklist @populate_tracklist
def test_move_single(self): def test_move_single(self):

View File

@ -16,22 +16,22 @@ STOPPED = PlaybackState.STOPPED
class PlaybackOptionsHandlerTest(protocol.BaseTestCase): class PlaybackOptionsHandlerTest(protocol.BaseTestCase):
def test_consume_off(self): def test_consume_off(self):
self.sendRequest('consume "0"') self.sendRequest('consume "0"')
self.assertFalse(self.core.playback.consume.get()) self.assertFalse(self.core.tracklist.consume.get())
self.assertInResponse('OK') self.assertInResponse('OK')
def test_consume_off_without_quotes(self): def test_consume_off_without_quotes(self):
self.sendRequest('consume 0') self.sendRequest('consume 0')
self.assertFalse(self.core.playback.consume.get()) self.assertFalse(self.core.tracklist.consume.get())
self.assertInResponse('OK') self.assertInResponse('OK')
def test_consume_on(self): def test_consume_on(self):
self.sendRequest('consume "1"') self.sendRequest('consume "1"')
self.assertTrue(self.core.playback.consume.get()) self.assertTrue(self.core.tracklist.consume.get())
self.assertInResponse('OK') self.assertInResponse('OK')
def test_consume_on_without_quotes(self): def test_consume_on_without_quotes(self):
self.sendRequest('consume 1') self.sendRequest('consume 1')
self.assertTrue(self.core.playback.consume.get()) self.assertTrue(self.core.tracklist.consume.get())
self.assertInResponse('OK') self.assertInResponse('OK')
def test_crossfade(self): def test_crossfade(self):
@ -40,42 +40,42 @@ class PlaybackOptionsHandlerTest(protocol.BaseTestCase):
def test_random_off(self): def test_random_off(self):
self.sendRequest('random "0"') self.sendRequest('random "0"')
self.assertFalse(self.core.playback.random.get()) self.assertFalse(self.core.tracklist.random.get())
self.assertInResponse('OK') self.assertInResponse('OK')
def test_random_off_without_quotes(self): def test_random_off_without_quotes(self):
self.sendRequest('random 0') self.sendRequest('random 0')
self.assertFalse(self.core.playback.random.get()) self.assertFalse(self.core.tracklist.random.get())
self.assertInResponse('OK') self.assertInResponse('OK')
def test_random_on(self): def test_random_on(self):
self.sendRequest('random "1"') self.sendRequest('random "1"')
self.assertTrue(self.core.playback.random.get()) self.assertTrue(self.core.tracklist.random.get())
self.assertInResponse('OK') self.assertInResponse('OK')
def test_random_on_without_quotes(self): def test_random_on_without_quotes(self):
self.sendRequest('random 1') self.sendRequest('random 1')
self.assertTrue(self.core.playback.random.get()) self.assertTrue(self.core.tracklist.random.get())
self.assertInResponse('OK') self.assertInResponse('OK')
def test_repeat_off(self): def test_repeat_off(self):
self.sendRequest('repeat "0"') self.sendRequest('repeat "0"')
self.assertFalse(self.core.playback.repeat.get()) self.assertFalse(self.core.tracklist.repeat.get())
self.assertInResponse('OK') self.assertInResponse('OK')
def test_repeat_off_without_quotes(self): def test_repeat_off_without_quotes(self):
self.sendRequest('repeat 0') self.sendRequest('repeat 0')
self.assertFalse(self.core.playback.repeat.get()) self.assertFalse(self.core.tracklist.repeat.get())
self.assertInResponse('OK') self.assertInResponse('OK')
def test_repeat_on(self): def test_repeat_on(self):
self.sendRequest('repeat "1"') self.sendRequest('repeat "1"')
self.assertTrue(self.core.playback.repeat.get()) self.assertTrue(self.core.tracklist.repeat.get())
self.assertInResponse('OK') self.assertInResponse('OK')
def test_repeat_on_without_quotes(self): def test_repeat_on_without_quotes(self):
self.sendRequest('repeat 1') self.sendRequest('repeat 1')
self.assertTrue(self.core.playback.repeat.get()) self.assertTrue(self.core.tracklist.repeat.get())
self.assertInResponse('OK') self.assertInResponse('OK')
def test_setvol_below_min(self): def test_setvol_below_min(self):
@ -115,22 +115,22 @@ class PlaybackOptionsHandlerTest(protocol.BaseTestCase):
def test_single_off(self): def test_single_off(self):
self.sendRequest('single "0"') self.sendRequest('single "0"')
self.assertFalse(self.core.playback.single.get()) self.assertFalse(self.core.tracklist.single.get())
self.assertInResponse('OK') self.assertInResponse('OK')
def test_single_off_without_quotes(self): def test_single_off_without_quotes(self):
self.sendRequest('single 0') self.sendRequest('single 0')
self.assertFalse(self.core.playback.single.get()) self.assertFalse(self.core.tracklist.single.get())
self.assertInResponse('OK') self.assertInResponse('OK')
def test_single_on(self): def test_single_on(self):
self.sendRequest('single "1"') self.sendRequest('single "1"')
self.assertTrue(self.core.playback.single.get()) self.assertTrue(self.core.tracklist.single.get())
self.assertInResponse('OK') self.assertInResponse('OK')
def test_single_on_without_quotes(self): def test_single_on_without_quotes(self):
self.sendRequest('single 1') self.sendRequest('single 1')
self.assertTrue(self.core.playback.single.get()) self.assertTrue(self.core.tracklist.single.get())
self.assertInResponse('OK') self.assertInResponse('OK')
def test_replay_gain_mode_off(self): def test_replay_gain_mode_off(self):

View File

@ -64,7 +64,7 @@ class StatusHandlerTest(unittest.TestCase):
self.assertEqual(int(result['repeat']), 0) self.assertEqual(int(result['repeat']), 0)
def test_status_method_contains_repeat_is_1(self): def test_status_method_contains_repeat_is_1(self):
self.core.playback.repeat = 1 self.core.tracklist.repeat = 1
result = dict(status.status(self.context)) result = dict(status.status(self.context))
self.assertIn('repeat', result) self.assertIn('repeat', result)
self.assertEqual(int(result['repeat']), 1) self.assertEqual(int(result['repeat']), 1)
@ -75,7 +75,7 @@ class StatusHandlerTest(unittest.TestCase):
self.assertEqual(int(result['random']), 0) self.assertEqual(int(result['random']), 0)
def test_status_method_contains_random_is_1(self): def test_status_method_contains_random_is_1(self):
self.core.playback.random = 1 self.core.tracklist.random = 1
result = dict(status.status(self.context)) result = dict(status.status(self.context))
self.assertIn('random', result) self.assertIn('random', result)
self.assertEqual(int(result['random']), 1) self.assertEqual(int(result['random']), 1)
@ -91,7 +91,7 @@ class StatusHandlerTest(unittest.TestCase):
self.assertEqual(int(result['consume']), 0) self.assertEqual(int(result['consume']), 0)
def test_status_method_contains_consume_is_1(self): def test_status_method_contains_consume_is_1(self):
self.core.playback.consume = 1 self.core.tracklist.consume = 1
result = dict(status.status(self.context)) result = dict(status.status(self.context))
self.assertIn('consume', result) self.assertIn('consume', result)
self.assertEqual(int(result['consume']), 1) self.assertEqual(int(result['consume']), 1)

View File

@ -34,6 +34,9 @@ class Calculator(object):
def _secret(self): def _secret(self):
return 'Grand Unified Theory' return 'Grand Unified Theory'
def fail(self):
raise ValueError('What did you expect?')
class JsonRpcTestBase(unittest.TestCase): class JsonRpcTestBase(unittest.TestCase):
def setUp(self): def setUp(self):
@ -266,12 +269,12 @@ class JsonRpcSingleNotificationTest(JsonRpcTestBase):
class JsonRpcBatchTest(JsonRpcTestBase): class JsonRpcBatchTest(JsonRpcTestBase):
def test_batch_of_only_commands_returns_all(self): def test_batch_of_only_commands_returns_all(self):
self.core.playback.set_random(True).get() self.core.tracklist.set_random(True).get()
request = [ request = [
{'jsonrpc': '2.0', 'method': 'core.playback.get_repeat', 'id': 1}, {'jsonrpc': '2.0', 'method': 'core.tracklist.get_repeat', 'id': 1},
{'jsonrpc': '2.0', 'method': 'core.playback.get_random', 'id': 2}, {'jsonrpc': '2.0', 'method': 'core.tracklist.get_random', 'id': 2},
{'jsonrpc': '2.0', 'method': 'core.playback.get_single', 'id': 3}, {'jsonrpc': '2.0', 'method': 'core.tracklist.get_single', 'id': 3},
] ]
response = self.jrw.handle_data(request) response = self.jrw.handle_data(request)
@ -283,12 +286,12 @@ class JsonRpcBatchTest(JsonRpcTestBase):
self.assertEqual(response[3]['result'], False) self.assertEqual(response[3]['result'], False)
def test_batch_of_commands_and_notifications_returns_some(self): def test_batch_of_commands_and_notifications_returns_some(self):
self.core.playback.set_random(True).get() self.core.tracklist.set_random(True).get()
request = [ request = [
{'jsonrpc': '2.0', 'method': 'core.playback.get_repeat'}, {'jsonrpc': '2.0', 'method': 'core.tracklist.get_repeat'},
{'jsonrpc': '2.0', 'method': 'core.playback.get_random', 'id': 2}, {'jsonrpc': '2.0', 'method': 'core.tracklist.get_random', 'id': 2},
{'jsonrpc': '2.0', 'method': 'core.playback.get_single', 'id': 3}, {'jsonrpc': '2.0', 'method': 'core.tracklist.get_single', 'id': 3},
] ]
response = self.jrw.handle_data(request) response = self.jrw.handle_data(request)
@ -300,12 +303,12 @@ class JsonRpcBatchTest(JsonRpcTestBase):
self.assertEqual(response[3]['result'], False) self.assertEqual(response[3]['result'], False)
def test_batch_of_only_notifications_returns_nothing(self): def test_batch_of_only_notifications_returns_nothing(self):
self.core.playback.set_random(True).get() self.core.tracklist.set_random(True).get()
request = [ request = [
{'jsonrpc': '2.0', 'method': 'core.playback.get_repeat'}, {'jsonrpc': '2.0', 'method': 'core.tracklist.get_repeat'},
{'jsonrpc': '2.0', 'method': 'core.playback.get_random'}, {'jsonrpc': '2.0', 'method': 'core.tracklist.get_random'},
{'jsonrpc': '2.0', 'method': 'core.playback.get_single'}, {'jsonrpc': '2.0', 'method': 'core.tracklist.get_single'},
] ]
response = self.jrw.handle_data(request) response = self.jrw.handle_data(request)
@ -316,8 +319,8 @@ class JsonRpcSingleCommandErrorTest(JsonRpcTestBase):
def test_application_error_response(self): def test_application_error_response(self):
request = { request = {
'jsonrpc': '2.0', 'jsonrpc': '2.0',
'method': 'core.tracklist.index', 'method': 'calc.fail',
'params': ['bogus'], 'params': [],
'id': 1, 'id': 1,
} }
response = self.jrw.handle_data(request) response = self.jrw.handle_data(request)
@ -330,7 +333,7 @@ class JsonRpcSingleCommandErrorTest(JsonRpcTestBase):
data = error['data'] data = error['data']
self.assertEqual(data['type'], 'ValueError') self.assertEqual(data['type'], 'ValueError')
self.assertIn('not in list', data['message']) self.assertIn('What did you expect?', data['message'])
self.assertIn('traceback', data) self.assertIn('traceback', data)
self.assertIn('Traceback (most recent call last):', data['traceback']) self.assertIn('Traceback (most recent call last):', data['traceback'])
@ -522,10 +525,10 @@ class JsonRpcBatchErrorTest(JsonRpcTestBase):
{'jsonrpc': '2.0', 'method': 'core.playback.set_volume', {'jsonrpc': '2.0', 'method': 'core.playback.set_volume',
'params': [47], 'id': '1'}, 'params': [47], 'id': '1'},
# Notification # Notification
{'jsonrpc': '2.0', 'method': 'core.playback.set_consume', {'jsonrpc': '2.0', 'method': 'core.tracklist.set_consume',
'params': [True]}, 'params': [True]},
# Call with positional params # Call with positional params
{'jsonrpc': '2.0', 'method': 'core.playback.set_repeat', {'jsonrpc': '2.0', 'method': 'core.tracklist.set_repeat',
'params': [False], 'id': '2'}, 'params': [False], 'id': '2'},
# Invalid request # Invalid request
{'foo': 'boo'}, {'foo': 'boo'},
@ -533,7 +536,7 @@ class JsonRpcBatchErrorTest(JsonRpcTestBase):
{'jsonrpc': '2.0', 'method': 'foo.get', {'jsonrpc': '2.0', 'method': 'foo.get',
'params': {'name': 'myself'}, 'id': '5'}, 'params': {'name': 'myself'}, 'id': '5'},
# Call without params # Call without params
{'jsonrpc': '2.0', 'method': 'core.playback.get_random', {'jsonrpc': '2.0', 'method': 'core.tracklist.get_random',
'id': '9'}, 'id': '9'},
] ]
response = self.jrw.handle_data(request) response = self.jrw.handle_data(request)