Merge branch 'develop' into feature/mpris-frontend
Conflicts: mopidy/backends/base/playback.py mopidy/listeners.py tests/backends/events_test.py tests/listeners_test.py
This commit is contained in:
commit
7a2984528c
@ -25,6 +25,9 @@ v0.6.0 (in development)
|
||||
- The local client now tries to lookup where your music is via XDG, it will
|
||||
fall-back to ``~/music`` or use whatever setting you set manually.
|
||||
|
||||
- The idle command is now supported by mopidy for the following subsystems:
|
||||
player, playlist, options and mixer (Fixes: :issue:`32`).
|
||||
|
||||
**Changes**
|
||||
|
||||
- Replace :attr:`mopidy.backends.base.Backend.uri_handlers` with
|
||||
|
||||
@ -2,6 +2,7 @@ from copy import copy
|
||||
import logging
|
||||
import random
|
||||
|
||||
from mopidy.listeners import BackendListener
|
||||
from mopidy.models import CpTrack
|
||||
|
||||
logger = logging.getLogger('mopidy.backends.base')
|
||||
@ -16,6 +17,7 @@ class CurrentPlaylistController(object):
|
||||
|
||||
def __init__(self, backend):
|
||||
self.backend = backend
|
||||
self.cp_id = 0
|
||||
self._cp_tracks = []
|
||||
self._version = 0
|
||||
|
||||
@ -53,8 +55,9 @@ class CurrentPlaylistController(object):
|
||||
def version(self, version):
|
||||
self._version = version
|
||||
self.backend.playback.on_current_playlist_change()
|
||||
self._trigger_playlist_changed()
|
||||
|
||||
def add(self, track, at_position=None):
|
||||
def add(self, track, at_position=None, increase_version=True):
|
||||
"""
|
||||
Add the track to the end of, or at the given position in the current
|
||||
playlist.
|
||||
@ -68,12 +71,14 @@ class CurrentPlaylistController(object):
|
||||
"""
|
||||
assert at_position <= len(self._cp_tracks), \
|
||||
u'at_position can not be greater than playlist length'
|
||||
cp_track = CpTrack(self.version, track)
|
||||
cp_track = CpTrack(self.cp_id, track)
|
||||
if at_position is not None:
|
||||
self._cp_tracks.insert(at_position, cp_track)
|
||||
else:
|
||||
self._cp_tracks.append(cp_track)
|
||||
self.version += 1
|
||||
if increase_version:
|
||||
self.version += 1
|
||||
self.cp_id += 1
|
||||
return cp_track
|
||||
|
||||
def append(self, tracks):
|
||||
@ -84,7 +89,10 @@ class CurrentPlaylistController(object):
|
||||
:type tracks: list of :class:`mopidy.models.Track`
|
||||
"""
|
||||
for track in tracks:
|
||||
self.add(track)
|
||||
self.add(track, increase_version=False)
|
||||
|
||||
if tracks:
|
||||
self.version += 1
|
||||
|
||||
def clear(self):
|
||||
"""Clear the current playlist."""
|
||||
@ -199,3 +207,7 @@ class CurrentPlaylistController(object):
|
||||
random.shuffle(shuffled)
|
||||
self._cp_tracks = before + shuffled + after
|
||||
self.version += 1
|
||||
|
||||
def _trigger_playlist_changed(self):
|
||||
logger.debug(u'Triggering playlist changed event')
|
||||
BackendListener.send('playlist_changed')
|
||||
|
||||
@ -8,6 +8,17 @@ from mopidy.listeners import BackendListener
|
||||
|
||||
logger = logging.getLogger('mopidy.backends.base')
|
||||
|
||||
|
||||
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:
|
||||
self._trigger_options_changed()
|
||||
return setattr(self, name, value)
|
||||
return property(get_option, set_option)
|
||||
|
||||
|
||||
class PlaybackController(object):
|
||||
"""
|
||||
:param backend: the backend
|
||||
@ -34,7 +45,7 @@ class PlaybackController(object):
|
||||
#: Tracks are removed from the playlist when they have been played.
|
||||
#: :class:`False`
|
||||
#: Tracks are not removed from the playlist.
|
||||
consume = False
|
||||
consume = option_wrapper('_consume', False)
|
||||
|
||||
#: The currently playing or selected track.
|
||||
#:
|
||||
@ -46,21 +57,21 @@ class PlaybackController(object):
|
||||
#: Tracks are selected at random from the playlist.
|
||||
#: :class:`False`
|
||||
#: Tracks are played in the order of the playlist.
|
||||
random = False
|
||||
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 = False
|
||||
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 = False
|
||||
single = option_wrapper('_single', False)
|
||||
|
||||
def __init__(self, backend, provider):
|
||||
self.backend = backend
|
||||
@ -276,6 +287,9 @@ class PlaybackController(object):
|
||||
def state(self, new_state):
|
||||
(old_state, self._state) = (self.state, new_state)
|
||||
logger.debug(u'Changing state: %s -> %s', old_state, new_state)
|
||||
|
||||
self._trigger_playback_state_changed()
|
||||
|
||||
# FIXME play_time stuff assumes backend does not have a better way of
|
||||
# handeling this stuff :/
|
||||
if (old_state in (self.PLAYING, self.STOPPED)
|
||||
@ -346,6 +360,7 @@ class PlaybackController(object):
|
||||
original_cp_track = self.current_cp_track
|
||||
|
||||
if self.cp_track_at_eot:
|
||||
self._trigger_track_playback_ended()
|
||||
self.play(self.cp_track_at_eot)
|
||||
else:
|
||||
self.stop(clear_current_track=True)
|
||||
@ -375,6 +390,7 @@ class PlaybackController(object):
|
||||
will continue. If it was paused, it will still be paused, etc.
|
||||
"""
|
||||
if self.cp_track_at_next:
|
||||
self._trigger_track_playback_ended()
|
||||
self.change_track(self.cp_track_at_next)
|
||||
else:
|
||||
self.stop(clear_current_track=True)
|
||||
@ -383,7 +399,7 @@ class PlaybackController(object):
|
||||
"""Pause playback."""
|
||||
if self.provider.pause():
|
||||
self.state = self.PAUSED
|
||||
self._trigger_paused_playing_event()
|
||||
self._trigger_track_playback_paused()
|
||||
|
||||
def play(self, cp_track=None, on_error_step=1):
|
||||
"""
|
||||
@ -405,11 +421,12 @@ class PlaybackController(object):
|
||||
return self.resume()
|
||||
elif self.current_cp_track is not None:
|
||||
cp_track = self.current_cp_track
|
||||
elif self.current_cp_track is None:
|
||||
elif self.current_cp_track is None and on_error_step == 1:
|
||||
cp_track = self.cp_track_at_next
|
||||
elif self.current_cp_track is None and on_error_step == -1:
|
||||
cp_track = self.cp_track_at_previous
|
||||
|
||||
if cp_track is not None:
|
||||
self.stop()
|
||||
self.current_cp_track = cp_track
|
||||
self.state = self.PLAYING
|
||||
if not self.provider.play(cp_track.track):
|
||||
@ -424,7 +441,7 @@ class PlaybackController(object):
|
||||
if self.random and self.current_cp_track in self._shuffled:
|
||||
self._shuffled.remove(self.current_cp_track)
|
||||
|
||||
self._trigger_started_playing_event()
|
||||
self._trigger_track_playback_started()
|
||||
|
||||
def previous(self):
|
||||
"""
|
||||
@ -433,16 +450,14 @@ class PlaybackController(object):
|
||||
The current playback state will be kept. If it was playing, playing
|
||||
will continue. If it was paused, it will still be paused, etc.
|
||||
"""
|
||||
if self.cp_track_at_previous is None:
|
||||
self.stop()
|
||||
else:
|
||||
self.change_track(self.cp_track_at_previous, on_error_step=-1)
|
||||
self._trigger_track_playback_ended()
|
||||
self.change_track(self.cp_track_at_previous, on_error_step=-1)
|
||||
|
||||
def resume(self):
|
||||
"""If paused, resume playing the current track."""
|
||||
if self.state == self.PAUSED and self.provider.resume():
|
||||
self.state = self.PLAYING
|
||||
self._trigger_resumed_playing_event()
|
||||
self._trigger_track_playback_resumed()
|
||||
|
||||
def seek(self, time_position):
|
||||
"""
|
||||
@ -480,65 +495,50 @@ class PlaybackController(object):
|
||||
:type clear_current_track: boolean
|
||||
"""
|
||||
if self.state != self.STOPPED:
|
||||
self._trigger_stopped_playing_event()
|
||||
if self.provider.stop():
|
||||
self._trigger_track_playback_ended()
|
||||
self.state = self.STOPPED
|
||||
if clear_current_track:
|
||||
self.current_cp_track = None
|
||||
|
||||
def _trigger_paused_playing_event(self):
|
||||
logger.debug(u'Triggering paused playing event')
|
||||
def _trigger_track_playback_paused(self):
|
||||
logger.debug(u'Triggering track playback paused event')
|
||||
if self.current_track is None:
|
||||
return
|
||||
ActorRegistry.broadcast({
|
||||
'command': 'pykka_call',
|
||||
'attr_path': ('paused_playing',),
|
||||
'args': [],
|
||||
'kwargs': {
|
||||
'track': self.current_track,
|
||||
'time_position': self.time_position,
|
||||
},
|
||||
}, target_class=BackendListener)
|
||||
BackendListener.send('track_playback_paused',
|
||||
track=self.current_track,
|
||||
time_position=self.time_position)
|
||||
|
||||
def _trigger_resumed_playing_event(self):
|
||||
logger.debug(u'Triggering resumed playing event')
|
||||
def _trigger_track_playback_resumed(self):
|
||||
logger.debug(u'Triggering track playback resumed event')
|
||||
if self.current_track is None:
|
||||
return
|
||||
ActorRegistry.broadcast({
|
||||
'command': 'pykka_call',
|
||||
'attr_path': ('resumed_playing',),
|
||||
'args': [],
|
||||
'kwargs': {
|
||||
'track': self.current_track,
|
||||
'time_position': self.time_position,
|
||||
},
|
||||
}, target_class=BackendListener)
|
||||
BackendListener.send('track_playback_resumed',
|
||||
track=self.current_track,
|
||||
time_position=self.time_position)
|
||||
|
||||
def _trigger_started_playing_event(self):
|
||||
logger.debug(u'Triggering started playing event')
|
||||
def _trigger_track_playback_started(self):
|
||||
logger.debug(u'Triggering track playback started event')
|
||||
if self.current_track is None:
|
||||
return
|
||||
ActorRegistry.broadcast({
|
||||
'command': 'pykka_call',
|
||||
'attr_path': ('started_playing',),
|
||||
'args': [],
|
||||
'kwargs': {'track': self.current_track},
|
||||
}, target_class=BackendListener)
|
||||
BackendListener.send('track_playback_started',
|
||||
track=self.current_track)
|
||||
|
||||
def _trigger_stopped_playing_event(self):
|
||||
# TODO Test that this is called on next/prev/end-of-track
|
||||
logger.debug(u'Triggering stopped playing event')
|
||||
def _trigger_track_playback_ended(self):
|
||||
logger.debug(u'Triggering track playback ended event')
|
||||
if self.current_track is None:
|
||||
return
|
||||
ActorRegistry.broadcast({
|
||||
'command': 'pykka_call',
|
||||
'attr_path': ('stopped_playing',),
|
||||
'args': [],
|
||||
'kwargs': {
|
||||
'track': self.current_track,
|
||||
'time_position': self.time_position,
|
||||
},
|
||||
}, target_class=BackendListener)
|
||||
BackendListener.send('track_playback_ended',
|
||||
track=self.current_track,
|
||||
time_position=self.time_position)
|
||||
|
||||
def _trigger_playback_state_changed(self):
|
||||
logger.debug(u'Triggering playback state change event')
|
||||
BackendListener.send('playback_state_changed')
|
||||
|
||||
def _trigger_options_changed(self):
|
||||
logger.debug(u'Triggering options changed event')
|
||||
BackendListener.send('options_changed')
|
||||
|
||||
|
||||
class BasePlaybackProvider(object):
|
||||
|
||||
@ -71,7 +71,7 @@ def parse_options():
|
||||
action='store_const', const=0, dest='verbosity_level',
|
||||
help='less output (warning level)')
|
||||
parser.add_option('-v', '--verbose',
|
||||
action='store_const', const=2, dest='verbosity_level',
|
||||
action='count', default=1, dest='verbosity_level',
|
||||
help='more output (debug level)')
|
||||
parser.add_option('--save-debug-log',
|
||||
action='store_true', dest='save_debug_log',
|
||||
|
||||
@ -57,7 +57,7 @@ class LastfmFrontend(ThreadingActor, BackendListener):
|
||||
logger.error(u'Error during Last.fm setup: %s', e)
|
||||
self.stop()
|
||||
|
||||
def started_playing(self, track):
|
||||
def track_playback_started(self, track):
|
||||
artists = ', '.join([a.name for a in track.artists])
|
||||
duration = track.length and track.length // 1000 or 0
|
||||
self.last_start_time = int(time.time())
|
||||
@ -74,7 +74,7 @@ class LastfmFrontend(ThreadingActor, BackendListener):
|
||||
pylast.MalformedResponseError, pylast.WSError) as e:
|
||||
logger.warning(u'Error submitting playing track to Last.fm: %s', e)
|
||||
|
||||
def stopped_playing(self, track, time_position):
|
||||
def track_playback_ended(self, track, time_position):
|
||||
artists = ', '.join([a.name for a in track.artists])
|
||||
duration = track.length and track.length // 1000 or 0
|
||||
time_position = time_position // 1000
|
||||
|
||||
@ -1,15 +1,15 @@
|
||||
import logging
|
||||
import sys
|
||||
|
||||
from pykka.actor import ThreadingActor
|
||||
from pykka import registry, actor
|
||||
|
||||
from mopidy import settings
|
||||
from mopidy import listeners, settings
|
||||
from mopidy.frontends.mpd import dispatcher, protocol
|
||||
from mopidy.utils import network, process, log
|
||||
|
||||
logger = logging.getLogger('mopidy.frontends.mpd')
|
||||
|
||||
class MpdFrontend(ThreadingActor):
|
||||
class MpdFrontend(actor.ThreadingActor, listeners.BackendListener):
|
||||
"""
|
||||
The MPD frontend.
|
||||
|
||||
@ -39,6 +39,28 @@ class MpdFrontend(ThreadingActor):
|
||||
def on_stop(self):
|
||||
process.stop_actors_by_class(MpdSession)
|
||||
|
||||
def send_idle(self, subsystem):
|
||||
# FIXME this should be updated once pykka supports non-blocking calls
|
||||
# on proxies or some similar solution
|
||||
registry.ActorRegistry.broadcast({
|
||||
'command': 'pykka_call',
|
||||
'attr_path': ('on_idle',),
|
||||
'args': [subsystem],
|
||||
'kwargs': {},
|
||||
}, target_class=MpdSession)
|
||||
|
||||
def playback_state_changed(self):
|
||||
self.send_idle('player')
|
||||
|
||||
def playlist_changed(self):
|
||||
self.send_idle('playlist')
|
||||
|
||||
def options_changed(self):
|
||||
self.send_idle('options')
|
||||
|
||||
def volume_changed(self):
|
||||
self.send_idle('mixer')
|
||||
|
||||
|
||||
class MpdSession(network.LineProtocol):
|
||||
"""
|
||||
@ -48,9 +70,10 @@ class MpdSession(network.LineProtocol):
|
||||
|
||||
terminator = protocol.LINE_TERMINATOR
|
||||
encoding = protocol.ENCODING
|
||||
delimeter = r'\r?\n'
|
||||
|
||||
def __init__(self, client):
|
||||
super(MpdSession, self).__init__(client)
|
||||
def __init__(self, connection):
|
||||
super(MpdSession, self).__init__(connection)
|
||||
self.dispatcher = dispatcher.MpdDispatcher(self)
|
||||
|
||||
def on_start(self):
|
||||
@ -67,8 +90,11 @@ class MpdSession(network.LineProtocol):
|
||||
|
||||
logger.debug(u'Response to [%s]:%s from %s: %s', self.host, self.port,
|
||||
self.actor_urn, log.indent(self.terminator.join(response)))
|
||||
|
||||
|
||||
self.send_lines(response)
|
||||
|
||||
def on_idle(self, subsystem):
|
||||
self.dispatcher.handle_idle(subsystem)
|
||||
|
||||
def close(self):
|
||||
self.stop()
|
||||
|
||||
@ -27,6 +27,8 @@ class MpdDispatcher(object):
|
||||
back to the MPD session.
|
||||
"""
|
||||
|
||||
_noidle = re.compile(r'^noidle$')
|
||||
|
||||
def __init__(self, session=None):
|
||||
self.authenticated = False
|
||||
self.command_list = False
|
||||
@ -42,11 +44,28 @@ class MpdDispatcher(object):
|
||||
self._catch_mpd_ack_errors_filter,
|
||||
self._authenticate_filter,
|
||||
self._command_list_filter,
|
||||
self._idle_filter,
|
||||
self._add_ok_filter,
|
||||
self._call_handler_filter,
|
||||
]
|
||||
return self._call_next_filter(request, response, filter_chain)
|
||||
|
||||
def handle_idle(self, subsystem):
|
||||
self.context.events.add(subsystem)
|
||||
|
||||
subsystems = self.context.subscriptions.intersection(
|
||||
self.context.events)
|
||||
if not subsystems:
|
||||
return
|
||||
|
||||
response = []
|
||||
for subsystem in subsystems:
|
||||
response.append(u'changed: %s' % subsystem)
|
||||
response.append(u'OK')
|
||||
self.context.subscriptions = set()
|
||||
self.context.events = set()
|
||||
self.context.session.send_lines(response)
|
||||
|
||||
def _call_next_filter(self, request, response, filter_chain):
|
||||
if filter_chain:
|
||||
next_filter = filter_chain.pop(0)
|
||||
@ -108,6 +127,29 @@ class MpdDispatcher(object):
|
||||
and request != u'command_list_end')
|
||||
|
||||
|
||||
### Filter: idle
|
||||
|
||||
def _idle_filter(self, request, response, filter_chain):
|
||||
if self._is_currently_idle() and not self._noidle.match(request):
|
||||
logger.debug(u'Client sent us %s, only %s is allowed while in '
|
||||
'the idle state', repr(request), repr(u'noidle'))
|
||||
self.context.session.close()
|
||||
return []
|
||||
|
||||
if not self._is_currently_idle() and self._noidle.match(request):
|
||||
return [] # noidle was called before idle
|
||||
|
||||
response = self._call_next_filter(request, response, filter_chain)
|
||||
|
||||
if self._is_currently_idle():
|
||||
return []
|
||||
else:
|
||||
return response
|
||||
|
||||
def _is_currently_idle(self):
|
||||
return bool(self.context.subscriptions)
|
||||
|
||||
|
||||
### Filter: add OK
|
||||
|
||||
def _add_ok_filter(self, request, response, filter_chain):
|
||||
@ -119,7 +161,6 @@ class MpdDispatcher(object):
|
||||
def _has_error(self, response):
|
||||
return response and response[-1].startswith(u'ACK')
|
||||
|
||||
|
||||
### Filter: call handler
|
||||
|
||||
def _call_handler_filter(self, request, response, filter_chain):
|
||||
@ -181,9 +222,17 @@ class MpdContext(object):
|
||||
#: The current :class:`mopidy.frontends.mpd.MpdSession`.
|
||||
session = None
|
||||
|
||||
#: The active subsystems that have pending events.
|
||||
events = None
|
||||
|
||||
#: The subsytems that we want to be notified about in idle mode.
|
||||
subscriptions = None
|
||||
|
||||
def __init__(self, dispatcher, session=None):
|
||||
self.dispatcher = dispatcher
|
||||
self.session = session
|
||||
self.events = set()
|
||||
self.subscriptions = set()
|
||||
self._backend = None
|
||||
self._mixer = None
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
from mopidy.frontends.mpd.protocol import handle_request
|
||||
|
||||
@handle_request(r'^$')
|
||||
@handle_request(r'^[ ]*$')
|
||||
def empty(context):
|
||||
"""The original MPD server returns ``OK`` on an empty request."""
|
||||
pass
|
||||
|
||||
@ -11,28 +11,16 @@ def commands(context):
|
||||
Shows which commands the current user has access to.
|
||||
"""
|
||||
if context.dispatcher.authenticated:
|
||||
command_names = [command.name for command in mpd_commands]
|
||||
command_names = set([command.name for command in mpd_commands])
|
||||
else:
|
||||
command_names = [command.name for command in mpd_commands
|
||||
if not command.auth_required]
|
||||
command_names = set([command.name for command in mpd_commands
|
||||
if not command.auth_required])
|
||||
|
||||
# No permission to use
|
||||
if 'kill' in command_names:
|
||||
command_names.remove('kill')
|
||||
|
||||
# Not shown by MPD in its command list
|
||||
if 'command_list_begin' in command_names:
|
||||
command_names.remove('command_list_begin')
|
||||
if 'command_list_ok_begin' in command_names:
|
||||
command_names.remove('command_list_ok_begin')
|
||||
if 'command_list_end' in command_names:
|
||||
command_names.remove('command_list_end')
|
||||
if 'idle' in command_names:
|
||||
command_names.remove('idle')
|
||||
if 'noidle' in command_names:
|
||||
command_names.remove('noidle')
|
||||
if 'sticker' in command_names:
|
||||
command_names.remove('sticker')
|
||||
# No one is permited to use kill, rest of commands are not listed by MPD,
|
||||
# so we shouldn't either.
|
||||
command_names = command_names - set(['kill', 'command_list_begin',
|
||||
'command_list_ok_begin', 'command_list_ok_begin', 'command_list_end',
|
||||
'idle', 'noidle', 'sticker'])
|
||||
|
||||
return [('command', command_name) for command_name in sorted(command_names)]
|
||||
|
||||
|
||||
@ -4,6 +4,10 @@ from mopidy.backends.base import PlaybackController
|
||||
from mopidy.frontends.mpd.protocol import handle_request
|
||||
from mopidy.frontends.mpd.exceptions import MpdNotImplemented
|
||||
|
||||
#: Subsystems that can be registered with idle command.
|
||||
SUBSYSTEMS = ['database', 'mixer', 'options', 'output',
|
||||
'player', 'playlist', 'stored_playlist', 'update', ]
|
||||
|
||||
@handle_request(r'^clearerror$')
|
||||
def clearerror(context):
|
||||
"""
|
||||
@ -67,12 +71,36 @@ def idle(context, subsystems=None):
|
||||
notifications when something changed in one of the specified
|
||||
subsystems.
|
||||
"""
|
||||
pass # TODO
|
||||
|
||||
if subsystems:
|
||||
subsystems = subsystems.split()
|
||||
else:
|
||||
subsystems = SUBSYSTEMS
|
||||
|
||||
for subsystem in subsystems:
|
||||
context.subscriptions.add(subsystem)
|
||||
|
||||
active = context.subscriptions.intersection(context.events)
|
||||
if not active:
|
||||
context.session.prevent_timeout = True
|
||||
return
|
||||
|
||||
response = []
|
||||
context.events = set()
|
||||
context.subscriptions = set()
|
||||
|
||||
for subsystem in active:
|
||||
response.append(u'changed: %s' % subsystem)
|
||||
return response
|
||||
|
||||
@handle_request(r'^noidle$')
|
||||
def noidle(context):
|
||||
"""See :meth:`_status_idle`."""
|
||||
pass # TODO
|
||||
if not context.subscriptions:
|
||||
return
|
||||
context.subscriptions = set()
|
||||
context.events = set()
|
||||
context.session.prevent_timeout = False
|
||||
|
||||
@handle_request(r'^stats$')
|
||||
def stats(context):
|
||||
@ -218,7 +246,13 @@ def _status_time(futures):
|
||||
_status_time_total(futures) // 1000)
|
||||
|
||||
def _status_time_elapsed(futures):
|
||||
return futures['playback.time_position'].get()
|
||||
time_position = futures['playback.time_position'].get()
|
||||
if time_position < 1000:
|
||||
# XXX ncmpcpp and mpc interpretes the elapsed time as seconds instead
|
||||
# of milliseconds if the elapsed time is less than approx. 1000.
|
||||
return 0
|
||||
else:
|
||||
return time_position
|
||||
|
||||
def _status_time_total(futures):
|
||||
current_cp_track = futures['playback.current_cp_track'].get()
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
from pykka import registry
|
||||
|
||||
class BackendListener(object):
|
||||
"""
|
||||
Marker interface for recipients of events sent by the backend.
|
||||
@ -9,9 +11,21 @@ class BackendListener(object):
|
||||
interested in all events.
|
||||
"""
|
||||
|
||||
def paused_playing(self, track, time_position):
|
||||
@staticmethod
|
||||
def send(event, **kwargs):
|
||||
"""Helper to allow calling of backend listener events"""
|
||||
# FIXME this should be updated once pykka supports non-blocking calls
|
||||
# on proxies or some similar solution
|
||||
registry.ActorRegistry.broadcast({
|
||||
'command': 'pykka_call',
|
||||
'attr_path': (event,),
|
||||
'args': [],
|
||||
'kwargs': kwargs,
|
||||
}, target_class=BackendListener)
|
||||
|
||||
def track_playback_paused(self, track, time_position):
|
||||
"""
|
||||
Called whenever playback is paused.
|
||||
Called whenever track playback is paused.
|
||||
|
||||
*MAY* be implemented by actor.
|
||||
|
||||
@ -22,9 +36,9 @@ class BackendListener(object):
|
||||
"""
|
||||
pass
|
||||
|
||||
def resumed_playing(self, track, time_position):
|
||||
def track_playback_resumed(self, track, time_position):
|
||||
"""
|
||||
Called whenever playback is resumed.
|
||||
Called whenever track playback is resumed.
|
||||
|
||||
*MAY* be implemented by actor.
|
||||
|
||||
@ -36,7 +50,7 @@ class BackendListener(object):
|
||||
pass
|
||||
|
||||
|
||||
def started_playing(self, track):
|
||||
def track_playback_started(self, track):
|
||||
"""
|
||||
Called whenever a new track starts playing.
|
||||
|
||||
@ -47,9 +61,9 @@ class BackendListener(object):
|
||||
"""
|
||||
pass
|
||||
|
||||
def stopped_playing(self, track, time_position):
|
||||
def track_playback_ended(self, track, time_position):
|
||||
"""
|
||||
Called whenever playback is stopped.
|
||||
Called whenever playback of a track ends.
|
||||
|
||||
*MAY* be implemented by actor.
|
||||
|
||||
@ -59,3 +73,35 @@ class BackendListener(object):
|
||||
:type time_position: int
|
||||
"""
|
||||
pass
|
||||
|
||||
def playback_state_changed(self):
|
||||
"""
|
||||
Called whenever playback state is changed.
|
||||
|
||||
*MAY* be implemented by actor.
|
||||
"""
|
||||
pass
|
||||
|
||||
def playlist_changed(self):
|
||||
"""
|
||||
Called whenever a playlist is changed.
|
||||
|
||||
*MAY* be implemented by actor.
|
||||
"""
|
||||
pass
|
||||
|
||||
def options_changed(self):
|
||||
"""
|
||||
Called whenever an option is changed.
|
||||
|
||||
*MAY* be implemented by actor.
|
||||
"""
|
||||
pass
|
||||
|
||||
def volume_changed(self):
|
||||
"""
|
||||
Called whenever the volume is changed.
|
||||
|
||||
*MAY* be implemented by actor.
|
||||
"""
|
||||
pass
|
||||
|
||||
@ -1,4 +1,8 @@
|
||||
from mopidy import settings
|
||||
import logging
|
||||
|
||||
from mopidy import listeners, settings
|
||||
|
||||
logger = logging.getLogger('mopdy.mixers')
|
||||
|
||||
class BaseMixer(object):
|
||||
"""
|
||||
@ -30,6 +34,7 @@ class BaseMixer(object):
|
||||
elif volume > 100:
|
||||
volume = 100
|
||||
self.set_volume(volume)
|
||||
self._trigger_volume_changed()
|
||||
|
||||
def get_volume(self):
|
||||
"""
|
||||
@ -46,3 +51,7 @@ class BaseMixer(object):
|
||||
*MUST be implemented by subclass.*
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def _trigger_volume_changed(self):
|
||||
logger.debug(u'Triggering volume changed event')
|
||||
listeners.BackendListener.send('volume_changed')
|
||||
|
||||
@ -20,7 +20,7 @@ def setup_console_logging(verbosity_level):
|
||||
if verbosity_level == 0:
|
||||
log_level = logging.WARNING
|
||||
log_format = settings.CONSOLE_LOG_FORMAT
|
||||
elif verbosity_level == 2:
|
||||
elif verbosity_level >= 2:
|
||||
log_level = logging.DEBUG
|
||||
log_format = settings.DEBUG_LOG_FORMAT
|
||||
else:
|
||||
@ -33,6 +33,9 @@ def setup_console_logging(verbosity_level):
|
||||
root = logging.getLogger('')
|
||||
root.addHandler(handler)
|
||||
|
||||
if verbosity_level < 3:
|
||||
logging.getLogger('pykka').setLevel(logging.INFO)
|
||||
|
||||
def setup_debug_logging_to_file():
|
||||
formatter = logging.Formatter(settings.DEBUG_LOG_FORMAT)
|
||||
handler = logging.handlers.RotatingFileHandler(
|
||||
|
||||
@ -278,16 +278,26 @@ class LineProtocol(ThreadingActor):
|
||||
then splitting data along line boundaries.
|
||||
"""
|
||||
|
||||
#: What terminator to use to split lines.
|
||||
#: Line terminator to use for outputed lines.
|
||||
terminator = '\n'
|
||||
|
||||
#: Regex to use for spliting lines, will be set compiled version of its
|
||||
#: own value, or to ``terminator``s value if it is not set itself.
|
||||
delimeter = None
|
||||
|
||||
#: What encoding to expect incomming data to be in, can be :class:`None`.
|
||||
encoding = 'utf-8'
|
||||
|
||||
def __init__(self, connection):
|
||||
self.connection = connection
|
||||
self.prevent_timeout = False
|
||||
self.recv_buffer = ''
|
||||
|
||||
if self.delimeter:
|
||||
self.delimeter = re.compile(self.delimeter)
|
||||
else:
|
||||
self.delimeter = re.compile(self.terminator)
|
||||
|
||||
@property
|
||||
def host(self):
|
||||
return self.connection.host
|
||||
@ -314,9 +324,11 @@ class LineProtocol(ThreadingActor):
|
||||
|
||||
for line in self.parse_lines():
|
||||
line = self.decode(line)
|
||||
self.on_line_received(line)
|
||||
if line is not None:
|
||||
self.on_line_received(line)
|
||||
|
||||
self.connection.enable_timeout()
|
||||
if not self.prevent_timeout:
|
||||
self.connection.enable_timeout()
|
||||
|
||||
def on_stop(self):
|
||||
"""Ensure that cleanup when actor stops."""
|
||||
@ -325,7 +337,7 @@ class LineProtocol(ThreadingActor):
|
||||
def parse_lines(self):
|
||||
"""Consume new data and yield any lines found."""
|
||||
while re.search(self.terminator, self.recv_buffer):
|
||||
line, self.recv_buffer = re.split(self.terminator,
|
||||
line, self.recv_buffer = self.delimeter.split(
|
||||
self.recv_buffer, 1)
|
||||
yield line
|
||||
|
||||
|
||||
@ -11,10 +11,10 @@ from mopidy.models import Track
|
||||
class BackendEventsTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.events = {
|
||||
'paused_playing': threading.Event(),
|
||||
'resumed_playing': threading.Event(),
|
||||
'started_playing': threading.Event(),
|
||||
'stopped_playing': threading.Event(),
|
||||
'track_playback_paused': threading.Event(),
|
||||
'track_playback_resumed': threading.Event(),
|
||||
'track_playback_started': threading.Event(),
|
||||
'track_playback_ended': threading.Event(),
|
||||
}
|
||||
self.backend = DummyBackend.start().proxy()
|
||||
self.listener = DummyBackendListener.start(self.events).proxy()
|
||||
@ -22,47 +22,47 @@ class BackendEventsTest(unittest.TestCase):
|
||||
def tearDown(self):
|
||||
ActorRegistry.stop_all()
|
||||
|
||||
def test_pause_sends_paused_playing_event(self):
|
||||
def test_pause_sends_track_playback_paused_event(self):
|
||||
self.backend.current_playlist.add([Track(uri='a')])
|
||||
self.backend.playback.play()
|
||||
self.backend.playback.pause()
|
||||
self.events['paused_playing'].wait(timeout=1)
|
||||
self.assertTrue(self.events['paused_playing'].is_set())
|
||||
self.events['track_playback_paused'].wait(timeout=1)
|
||||
self.assertTrue(self.events['track_playback_paused'].is_set())
|
||||
|
||||
def test_resume_sends_resumed_playing_event(self):
|
||||
def test_resume_sends_track_playback_resumed(self):
|
||||
self.backend.current_playlist.add([Track(uri='a')])
|
||||
self.backend.playback.play()
|
||||
self.backend.playback.pause()
|
||||
self.backend.playback.resume()
|
||||
self.events['resumed_playing'].wait(timeout=1)
|
||||
self.assertTrue(self.events['resumed_playing'].is_set())
|
||||
self.events['track_playback_resumed'].wait(timeout=1)
|
||||
self.assertTrue(self.events['track_playback_resumed'].is_set())
|
||||
|
||||
def test_play_sends_started_playing_event(self):
|
||||
def test_play_sends_track_playback_started_event(self):
|
||||
self.backend.current_playlist.add([Track(uri='a')])
|
||||
self.backend.playback.play()
|
||||
self.events['started_playing'].wait(timeout=1)
|
||||
self.assertTrue(self.events['started_playing'].is_set())
|
||||
self.events['track_playback_started'].wait(timeout=1)
|
||||
self.assertTrue(self.events['track_playback_started'].is_set())
|
||||
|
||||
def test_stop_sends_stopped_playing_event(self):
|
||||
def test_stop_sends_track_playback_ended_event(self):
|
||||
self.backend.current_playlist.add([Track(uri='a')])
|
||||
self.backend.playback.play()
|
||||
self.backend.playback.stop()
|
||||
self.events['stopped_playing'].wait(timeout=1)
|
||||
self.assertTrue(self.events['stopped_playing'].is_set())
|
||||
self.events['track_playback_ended'].wait(timeout=1)
|
||||
self.assertTrue(self.events['track_playback_ended'].is_set())
|
||||
|
||||
|
||||
class DummyBackendListener(ThreadingActor, BackendListener):
|
||||
def __init__(self, events):
|
||||
self.events = events
|
||||
|
||||
def paused_playing(self, track, time_position):
|
||||
self.events['paused_playing'].set()
|
||||
def track_playback_paused(self, track, time_position):
|
||||
self.events['track_playback_paused'].set()
|
||||
|
||||
def resumed_playing(self, track, time_position):
|
||||
self.events['resumed_playing'].set()
|
||||
def track_playback_resumed(self, track, time_position):
|
||||
self.events['track_playback_resumed'].set()
|
||||
|
||||
def started_playing(self, track):
|
||||
self.events['started_playing'].set()
|
||||
def track_playback_started(self, track):
|
||||
self.events['track_playback_started'].set()
|
||||
|
||||
def stopped_playing(self, track, time_position):
|
||||
self.events['stopped_playing'].set()
|
||||
def track_playback_ended(self, track, time_position):
|
||||
self.events['track_playback_ended'].set()
|
||||
|
||||
@ -1,30 +0,0 @@
|
||||
import unittest
|
||||
|
||||
from mopidy.backends.dummy import DummyBackend
|
||||
from mopidy.frontends.mpd.dispatcher import MpdDispatcher
|
||||
from mopidy.mixers.dummy import DummyMixer
|
||||
|
||||
class AudioOutputHandlerTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.backend = DummyBackend.start().proxy()
|
||||
self.mixer = DummyMixer.start().proxy()
|
||||
self.dispatcher = MpdDispatcher()
|
||||
|
||||
def tearDown(self):
|
||||
self.backend.stop().get()
|
||||
self.mixer.stop().get()
|
||||
|
||||
def test_enableoutput(self):
|
||||
result = self.dispatcher.handle_request(u'enableoutput "0"')
|
||||
self.assert_(u'ACK [0@0] {} Not implemented' in result)
|
||||
|
||||
def test_disableoutput(self):
|
||||
result = self.dispatcher.handle_request(u'disableoutput "0"')
|
||||
self.assert_(u'ACK [0@0] {} Not implemented' in result)
|
||||
|
||||
def test_outputs(self):
|
||||
result = self.dispatcher.handle_request(u'outputs')
|
||||
self.assert_(u'outputid: 0' in result)
|
||||
self.assert_(u'outputname: None' in result)
|
||||
self.assert_(u'outputenabled: 1' in result)
|
||||
self.assert_(u'OK' in result)
|
||||
@ -1,63 +0,0 @@
|
||||
import unittest
|
||||
|
||||
from mopidy.backends.dummy import DummyBackend
|
||||
from mopidy.frontends.mpd import dispatcher
|
||||
from mopidy.mixers.dummy import DummyMixer
|
||||
|
||||
class CommandListsTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.b = DummyBackend.start().proxy()
|
||||
self.mixer = DummyMixer.start().proxy()
|
||||
self.dispatcher = dispatcher.MpdDispatcher()
|
||||
|
||||
def tearDown(self):
|
||||
self.b.stop().get()
|
||||
self.mixer.stop().get()
|
||||
|
||||
def test_command_list_begin(self):
|
||||
result = self.dispatcher.handle_request(u'command_list_begin')
|
||||
self.assertEquals(result, [])
|
||||
|
||||
def test_command_list_end(self):
|
||||
self.dispatcher.handle_request(u'command_list_begin')
|
||||
result = self.dispatcher.handle_request(u'command_list_end')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_command_list_end_without_start_first_is_an_unknown_command(self):
|
||||
result = self.dispatcher.handle_request(u'command_list_end')
|
||||
self.assertEquals(result[0],
|
||||
u'ACK [5@0] {} unknown command "command_list_end"')
|
||||
|
||||
def test_command_list_with_ping(self):
|
||||
self.dispatcher.handle_request(u'command_list_begin')
|
||||
self.assertEqual([], self.dispatcher.command_list)
|
||||
self.assertEqual(False, self.dispatcher.command_list_ok)
|
||||
self.dispatcher.handle_request(u'ping')
|
||||
self.assert_(u'ping' in self.dispatcher.command_list)
|
||||
result = self.dispatcher.handle_request(u'command_list_end')
|
||||
self.assert_(u'OK' in result)
|
||||
self.assertEqual(False, self.dispatcher.command_list)
|
||||
|
||||
def test_command_list_with_error_returns_ack_with_correct_index(self):
|
||||
self.dispatcher.handle_request(u'command_list_begin')
|
||||
self.dispatcher.handle_request(u'play') # Known command
|
||||
self.dispatcher.handle_request(u'paly') # Unknown command
|
||||
result = self.dispatcher.handle_request(u'command_list_end')
|
||||
self.assertEqual(len(result), 1, result)
|
||||
self.assertEqual(result[0], u'ACK [5@1] {} unknown command "paly"')
|
||||
|
||||
def test_command_list_ok_begin(self):
|
||||
result = self.dispatcher.handle_request(u'command_list_ok_begin')
|
||||
self.assertEquals(result, [])
|
||||
|
||||
def test_command_list_ok_with_ping(self):
|
||||
self.dispatcher.handle_request(u'command_list_ok_begin')
|
||||
self.assertEqual([], self.dispatcher.command_list)
|
||||
self.assertEqual(True, self.dispatcher.command_list_ok)
|
||||
self.dispatcher.handle_request(u'ping')
|
||||
self.assert_(u'ping' in self.dispatcher.command_list)
|
||||
result = self.dispatcher.handle_request(u'command_list_end')
|
||||
self.assert_(u'list_OK' in result)
|
||||
self.assert_(u'OK' in result)
|
||||
self.assertEqual(False, self.dispatcher.command_list)
|
||||
self.assertEqual(False, self.dispatcher.command_list_ok)
|
||||
@ -1,53 +0,0 @@
|
||||
import mock
|
||||
import unittest
|
||||
|
||||
from mopidy import settings
|
||||
from mopidy.backends.dummy import DummyBackend
|
||||
from mopidy.frontends.mpd import MpdSession
|
||||
from mopidy.frontends.mpd.dispatcher import MpdDispatcher
|
||||
from mopidy.mixers.dummy import DummyMixer
|
||||
|
||||
class ConnectionHandlerTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.backend = DummyBackend.start().proxy()
|
||||
self.mixer = DummyMixer.start().proxy()
|
||||
self.session = mock.Mock(spec=MpdSession)
|
||||
self.dispatcher = MpdDispatcher(session=self.session)
|
||||
|
||||
def tearDown(self):
|
||||
self.backend.stop().get()
|
||||
self.mixer.stop().get()
|
||||
settings.runtime.clear()
|
||||
|
||||
def test_close_closes_the_client_connection(self):
|
||||
result = self.dispatcher.handle_request(u'close')
|
||||
self.assert_(self.session.close.called,
|
||||
u'Should call close() on MpdSession')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_empty_request(self):
|
||||
result = self.dispatcher.handle_request(u'')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_kill(self):
|
||||
result = self.dispatcher.handle_request(u'kill')
|
||||
self.assert_(u'ACK [4@0] {kill} you don\'t have permission for "kill"' in result)
|
||||
|
||||
def test_valid_password_is_accepted(self):
|
||||
settings.MPD_SERVER_PASSWORD = u'topsecret'
|
||||
result = self.dispatcher.handle_request(u'password "topsecret"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_invalid_password_is_not_accepted(self):
|
||||
settings.MPD_SERVER_PASSWORD = u'topsecret'
|
||||
result = self.dispatcher.handle_request(u'password "secret"')
|
||||
self.assert_(u'ACK [3@0] {password} incorrect password' in result)
|
||||
|
||||
def test_any_password_is_not_accepted_when_password_check_turned_off(self):
|
||||
settings.MPD_SERVER_PASSWORD = None
|
||||
result = self.dispatcher.handle_request(u'password "secret"')
|
||||
self.assert_(u'ACK [3@0] {password} incorrect password' in result)
|
||||
|
||||
def test_ping(self):
|
||||
result = self.dispatcher.handle_request(u'ping')
|
||||
self.assert_(u'OK' in result)
|
||||
@ -1,412 +0,0 @@
|
||||
import unittest
|
||||
|
||||
from mopidy.backends.dummy import DummyBackend
|
||||
from mopidy.frontends.mpd.dispatcher import MpdDispatcher
|
||||
from mopidy.mixers.dummy import DummyMixer
|
||||
|
||||
class MusicDatabaseHandlerTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.backend = DummyBackend.start().proxy()
|
||||
self.mixer = DummyMixer.start().proxy()
|
||||
self.dispatcher = MpdDispatcher()
|
||||
|
||||
def tearDown(self):
|
||||
self.backend.stop().get()
|
||||
self.mixer.stop().get()
|
||||
|
||||
def test_count(self):
|
||||
result = self.dispatcher.handle_request(u'count "tag" "needle"')
|
||||
self.assert_(u'songs: 0' in result)
|
||||
self.assert_(u'playtime: 0' in result)
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_findadd(self):
|
||||
result = self.dispatcher.handle_request(u'findadd "album" "what"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_listall(self):
|
||||
result = self.dispatcher.handle_request(
|
||||
u'listall "file:///dev/urandom"')
|
||||
self.assert_(u'ACK [0@0] {} Not implemented' in result)
|
||||
|
||||
def test_listallinfo(self):
|
||||
result = self.dispatcher.handle_request(
|
||||
u'listallinfo "file:///dev/urandom"')
|
||||
self.assert_(u'ACK [0@0] {} Not implemented' in result)
|
||||
|
||||
def test_lsinfo_without_path_returns_same_as_listplaylists(self):
|
||||
lsinfo_result = self.dispatcher.handle_request(u'lsinfo')
|
||||
listplaylists_result = self.dispatcher.handle_request(u'listplaylists')
|
||||
self.assertEqual(lsinfo_result, listplaylists_result)
|
||||
|
||||
def test_lsinfo_with_empty_path_returns_same_as_listplaylists(self):
|
||||
lsinfo_result = self.dispatcher.handle_request(u'lsinfo ""')
|
||||
listplaylists_result = self.dispatcher.handle_request(u'listplaylists')
|
||||
self.assertEqual(lsinfo_result, listplaylists_result)
|
||||
|
||||
def test_lsinfo_for_root_returns_same_as_listplaylists(self):
|
||||
lsinfo_result = self.dispatcher.handle_request(u'lsinfo "/"')
|
||||
listplaylists_result = self.dispatcher.handle_request(u'listplaylists')
|
||||
self.assertEqual(lsinfo_result, listplaylists_result)
|
||||
|
||||
def test_update_without_uri(self):
|
||||
result = self.dispatcher.handle_request(u'update')
|
||||
self.assert_(u'OK' in result)
|
||||
self.assert_(u'updating_db: 0' in result)
|
||||
|
||||
def test_update_with_uri(self):
|
||||
result = self.dispatcher.handle_request(u'update "file:///dev/urandom"')
|
||||
self.assert_(u'OK' in result)
|
||||
self.assert_(u'updating_db: 0' in result)
|
||||
|
||||
def test_rescan_without_uri(self):
|
||||
result = self.dispatcher.handle_request(u'rescan')
|
||||
self.assert_(u'OK' in result)
|
||||
self.assert_(u'updating_db: 0' in result)
|
||||
|
||||
def test_rescan_with_uri(self):
|
||||
result = self.dispatcher.handle_request(u'rescan "file:///dev/urandom"')
|
||||
self.assert_(u'OK' in result)
|
||||
self.assert_(u'updating_db: 0' in result)
|
||||
|
||||
|
||||
class MusicDatabaseFindTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.backend = DummyBackend.start().proxy()
|
||||
self.mixer = DummyMixer.start().proxy()
|
||||
self.dispatcher = MpdDispatcher()
|
||||
|
||||
def tearDown(self):
|
||||
self.backend.stop().get()
|
||||
self.mixer.stop().get()
|
||||
|
||||
def test_find_album(self):
|
||||
result = self.dispatcher.handle_request(u'find "album" "what"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_find_album_without_quotes(self):
|
||||
result = self.dispatcher.handle_request(u'find album "what"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_find_artist(self):
|
||||
result = self.dispatcher.handle_request(u'find "artist" "what"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_find_artist_without_quotes(self):
|
||||
result = self.dispatcher.handle_request(u'find artist "what"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_find_title(self):
|
||||
result = self.dispatcher.handle_request(u'find "title" "what"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_find_title_without_quotes(self):
|
||||
result = self.dispatcher.handle_request(u'find title "what"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_find_date(self):
|
||||
result = self.dispatcher.handle_request(u'find "date" "2002-01-01"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_find_date_without_quotes(self):
|
||||
result = self.dispatcher.handle_request(u'find date "2002-01-01"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_find_date_with_capital_d_and_incomplete_date(self):
|
||||
result = self.dispatcher.handle_request(u'find Date "2005"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_find_else_should_fail(self):
|
||||
|
||||
result = self.dispatcher.handle_request(u'find "somethingelse" "what"')
|
||||
self.assertEqual(result[0], u'ACK [2@0] {find} incorrect arguments')
|
||||
|
||||
def test_find_album_and_artist(self):
|
||||
result = self.dispatcher.handle_request(
|
||||
u'find album "album_what" artist "artist_what"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
|
||||
class MusicDatabaseListTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.backend = DummyBackend.start().proxy()
|
||||
self.mixer = DummyMixer.start().proxy()
|
||||
self.dispatcher = MpdDispatcher()
|
||||
|
||||
def tearDown(self):
|
||||
self.backend.stop().get()
|
||||
self.mixer.stop().get()
|
||||
|
||||
def test_list_foo_returns_ack(self):
|
||||
result = self.dispatcher.handle_request(u'list "foo"')
|
||||
self.assertEqual(result[0],
|
||||
u'ACK [2@0] {list} incorrect arguments')
|
||||
|
||||
### Artist
|
||||
|
||||
def test_list_artist_with_quotes(self):
|
||||
result = self.dispatcher.handle_request(u'list "artist"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_list_artist_without_quotes(self):
|
||||
result = self.dispatcher.handle_request(u'list artist')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_list_artist_without_quotes_and_capitalized(self):
|
||||
result = self.dispatcher.handle_request(u'list Artist')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_list_artist_with_query_of_one_token(self):
|
||||
result = self.dispatcher.handle_request(u'list "artist" "anartist"')
|
||||
self.assertEqual(result[0],
|
||||
u'ACK [2@0] {list} should be "Album" for 3 arguments')
|
||||
|
||||
def test_list_artist_with_unknown_field_in_query_returns_ack(self):
|
||||
result = self.dispatcher.handle_request(u'list "artist" "foo" "bar"')
|
||||
self.assertEqual(result[0],
|
||||
u'ACK [2@0] {list} not able to parse args')
|
||||
|
||||
def test_list_artist_by_artist(self):
|
||||
result = self.dispatcher.handle_request(
|
||||
u'list "artist" "artist" "anartist"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_list_artist_by_album(self):
|
||||
result = self.dispatcher.handle_request(
|
||||
u'list "artist" "album" "analbum"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_list_artist_by_full_date(self):
|
||||
result = self.dispatcher.handle_request(
|
||||
u'list "artist" "date" "2001-01-01"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_list_artist_by_year(self):
|
||||
result = self.dispatcher.handle_request(
|
||||
u'list "artist" "date" "2001"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_list_artist_by_genre(self):
|
||||
result = self.dispatcher.handle_request(
|
||||
u'list "artist" "genre" "agenre"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_list_artist_by_artist_and_album(self):
|
||||
result = self.dispatcher.handle_request(
|
||||
u'list "artist" "artist" "anartist" "album" "analbum"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
### Album
|
||||
|
||||
def test_list_album_with_quotes(self):
|
||||
result = self.dispatcher.handle_request(u'list "album"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_list_album_without_quotes(self):
|
||||
result = self.dispatcher.handle_request(u'list album')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_list_album_without_quotes_and_capitalized(self):
|
||||
result = self.dispatcher.handle_request(u'list Album')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_list_album_with_artist_name(self):
|
||||
result = self.dispatcher.handle_request(u'list "album" "anartist"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_list_album_by_artist(self):
|
||||
result = self.dispatcher.handle_request(
|
||||
u'list "album" "artist" "anartist"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_list_album_by_album(self):
|
||||
result = self.dispatcher.handle_request(
|
||||
u'list "album" "album" "analbum"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_list_album_by_full_date(self):
|
||||
result = self.dispatcher.handle_request(
|
||||
u'list "album" "date" "2001-01-01"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_list_album_by_year(self):
|
||||
result = self.dispatcher.handle_request(
|
||||
u'list "album" "date" "2001"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_list_album_by_genre(self):
|
||||
result = self.dispatcher.handle_request(
|
||||
u'list "album" "genre" "agenre"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_list_album_by_artist_and_album(self):
|
||||
result = self.dispatcher.handle_request(
|
||||
u'list "album" "artist" "anartist" "album" "analbum"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
### Date
|
||||
|
||||
def test_list_date_with_quotes(self):
|
||||
result = self.dispatcher.handle_request(u'list "date"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_list_date_without_quotes(self):
|
||||
result = self.dispatcher.handle_request(u'list date')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_list_date_without_quotes_and_capitalized(self):
|
||||
result = self.dispatcher.handle_request(u'list Date')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_list_date_with_query_of_one_token(self):
|
||||
result = self.dispatcher.handle_request(u'list "date" "anartist"')
|
||||
self.assertEqual(result[0],
|
||||
u'ACK [2@0] {list} should be "Album" for 3 arguments')
|
||||
|
||||
def test_list_date_by_artist(self):
|
||||
result = self.dispatcher.handle_request(
|
||||
u'list "date" "artist" "anartist"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_list_date_by_album(self):
|
||||
result = self.dispatcher.handle_request(
|
||||
u'list "date" "album" "analbum"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_list_date_by_full_date(self):
|
||||
result = self.dispatcher.handle_request(
|
||||
u'list "date" "date" "2001-01-01"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_list_date_by_year(self):
|
||||
result = self.dispatcher.handle_request(u'list "date" "date" "2001"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_list_date_by_genre(self):
|
||||
result = self.dispatcher.handle_request(u'list "date" "genre" "agenre"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_list_date_by_artist_and_album(self):
|
||||
result = self.dispatcher.handle_request(
|
||||
u'list "date" "artist" "anartist" "album" "analbum"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
### Genre
|
||||
|
||||
def test_list_genre_with_quotes(self):
|
||||
result = self.dispatcher.handle_request(u'list "genre"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_list_genre_without_quotes(self):
|
||||
result = self.dispatcher.handle_request(u'list genre')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_list_genre_without_quotes_and_capitalized(self):
|
||||
result = self.dispatcher.handle_request(u'list Genre')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_list_genre_with_query_of_one_token(self):
|
||||
result = self.dispatcher.handle_request(u'list "genre" "anartist"')
|
||||
self.assertEqual(result[0],
|
||||
u'ACK [2@0] {list} should be "Album" for 3 arguments')
|
||||
|
||||
def test_list_genre_by_artist(self):
|
||||
result = self.dispatcher.handle_request(
|
||||
u'list "genre" "artist" "anartist"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_list_genre_by_album(self):
|
||||
result = self.dispatcher.handle_request(
|
||||
u'list "genre" "album" "analbum"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_list_genre_by_full_date(self):
|
||||
result = self.dispatcher.handle_request(
|
||||
u'list "genre" "date" "2001-01-01"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_list_genre_by_year(self):
|
||||
result = self.dispatcher.handle_request(
|
||||
u'list "genre" "date" "2001"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_list_genre_by_genre(self):
|
||||
result = self.dispatcher.handle_request(
|
||||
u'list "genre" "genre" "agenre"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_list_genre_by_artist_and_album(self):
|
||||
result = self.dispatcher.handle_request(
|
||||
u'list "genre" "artist" "anartist" "album" "analbum"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
|
||||
class MusicDatabaseSearchTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.backend = DummyBackend.start().proxy()
|
||||
self.mixer = DummyMixer.start().proxy()
|
||||
self.dispatcher = MpdDispatcher()
|
||||
|
||||
def tearDown(self):
|
||||
self.backend.stop().get()
|
||||
self.mixer.stop().get()
|
||||
|
||||
def test_search_album(self):
|
||||
result = self.dispatcher.handle_request(u'search "album" "analbum"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_search_album_without_quotes(self):
|
||||
result = self.dispatcher.handle_request(u'search album "analbum"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_search_artist(self):
|
||||
result = self.dispatcher.handle_request(u'search "artist" "anartist"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_search_artist_without_quotes(self):
|
||||
result = self.dispatcher.handle_request(u'search artist "anartist"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_search_filename(self):
|
||||
result = self.dispatcher.handle_request(
|
||||
u'search "filename" "afilename"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_search_filename_without_quotes(self):
|
||||
result = self.dispatcher.handle_request(u'search filename "afilename"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_search_title(self):
|
||||
result = self.dispatcher.handle_request(u'search "title" "atitle"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_search_title_without_quotes(self):
|
||||
result = self.dispatcher.handle_request(u'search title "atitle"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_search_any(self):
|
||||
result = self.dispatcher.handle_request(u'search "any" "anything"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_search_any_without_quotes(self):
|
||||
result = self.dispatcher.handle_request(u'search any "anything"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_search_date(self):
|
||||
result = self.dispatcher.handle_request(u'search "date" "2002-01-01"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_search_date_without_quotes(self):
|
||||
result = self.dispatcher.handle_request(u'search date "2002-01-01"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_search_date_with_capital_d_and_incomplete_date(self):
|
||||
result = self.dispatcher.handle_request(u'search Date "2005"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_search_else_should_fail(self):
|
||||
result = self.dispatcher.handle_request(
|
||||
u'search "sometype" "something"')
|
||||
self.assertEqual(result[0], u'ACK [2@0] {search} incorrect arguments')
|
||||
|
||||
|
||||
61
tests/frontends/mpd/protocol/__init__.py
Normal file
61
tests/frontends/mpd/protocol/__init__.py
Normal file
@ -0,0 +1,61 @@
|
||||
import unittest
|
||||
import mock
|
||||
|
||||
from mopidy import settings
|
||||
from mopidy.backends import dummy as backend
|
||||
from mopidy.frontends import mpd
|
||||
from mopidy.mixers import dummy as mixer
|
||||
|
||||
|
||||
class MockConnetion(mock.Mock):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(MockConnetion, self).__init__(*args, **kwargs)
|
||||
self.host = mock.sentinel.host
|
||||
self.port = mock.sentinel.port
|
||||
self.response = []
|
||||
|
||||
def send(self, data):
|
||||
lines = (line for line in data.split('\n') if line)
|
||||
self.response.extend(lines)
|
||||
|
||||
|
||||
class BaseTestCase(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.backend = backend.DummyBackend.start().proxy()
|
||||
self.mixer = mixer.DummyMixer.start().proxy()
|
||||
|
||||
self.connection = MockConnetion()
|
||||
self.session = mpd.MpdSession(self.connection)
|
||||
self.dispatcher = self.session.dispatcher
|
||||
self.context = self.dispatcher.context
|
||||
|
||||
def tearDown(self):
|
||||
self.backend.stop().get()
|
||||
self.mixer.stop().get()
|
||||
settings.runtime.clear()
|
||||
|
||||
def sendRequest(self, request):
|
||||
self.connection.response = []
|
||||
request = '%s\n' % request.encode('utf-8')
|
||||
self.session.on_receive({'received': request})
|
||||
return self.connection.response
|
||||
|
||||
def assertNoResponse(self):
|
||||
self.assertEqual([], self.connection.response)
|
||||
|
||||
def assertInResponse(self, value):
|
||||
self.assert_(value in self.connection.response, u'Did not find %s '
|
||||
'in %s' % (repr(value), repr(self.connection.response)))
|
||||
|
||||
def assertOnceInResponse(self, value):
|
||||
matched = len([r for r in self.connection.response if r == value])
|
||||
self.assertEqual(1, matched, 'Expected to find %s once in %s' %
|
||||
(repr(value), repr(self.connection.response)))
|
||||
|
||||
def assertNotInResponse(self, value):
|
||||
self.assert_(value not in self.connection.response, u'Found %s in %s' %
|
||||
(repr(value), repr(self.connection.response)))
|
||||
|
||||
def assertEqualResponse(self, value):
|
||||
self.assertEqual(1, len(self.connection.response))
|
||||
self.assertEqual(value, self.connection.response[0])
|
||||
17
tests/frontends/mpd/protocol/audio_output_test.py
Normal file
17
tests/frontends/mpd/protocol/audio_output_test.py
Normal file
@ -0,0 +1,17 @@
|
||||
from tests.frontends.mpd import protocol
|
||||
|
||||
class AudioOutputHandlerTest(protocol.BaseTestCase):
|
||||
def test_enableoutput(self):
|
||||
self.sendRequest(u'enableoutput "0"')
|
||||
self.assertInResponse(u'ACK [0@0] {} Not implemented')
|
||||
|
||||
def test_disableoutput(self):
|
||||
self.sendRequest(u'disableoutput "0"')
|
||||
self.assertInResponse(u'ACK [0@0] {} Not implemented')
|
||||
|
||||
def test_outputs(self):
|
||||
self.sendRequest(u'outputs')
|
||||
self.assertInResponse(u'outputid: 0')
|
||||
self.assertInResponse(u'outputname: None')
|
||||
self.assertInResponse(u'outputenabled: 1')
|
||||
self.assertInResponse(u'OK')
|
||||
@ -1,63 +1,61 @@
|
||||
import mock
|
||||
import unittest
|
||||
|
||||
from mopidy import settings
|
||||
from mopidy.frontends.mpd import MpdSession
|
||||
from mopidy.frontends.mpd.dispatcher import MpdDispatcher
|
||||
|
||||
class AuthenticationTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.session = mock.Mock(spec=MpdSession)
|
||||
self.dispatcher = MpdDispatcher(session=self.session)
|
||||
|
||||
def tearDown(self):
|
||||
settings.runtime.clear()
|
||||
from tests.frontends.mpd import protocol
|
||||
|
||||
class AuthenticationTest(protocol.BaseTestCase):
|
||||
def test_authentication_with_valid_password_is_accepted(self):
|
||||
settings.MPD_SERVER_PASSWORD = u'topsecret'
|
||||
response = self.dispatcher.handle_request(u'password "topsecret"')
|
||||
|
||||
self.sendRequest(u'password "topsecret"')
|
||||
self.assertTrue(self.dispatcher.authenticated)
|
||||
self.assert_(u'OK' in response)
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_authentication_with_invalid_password_is_not_accepted(self):
|
||||
settings.MPD_SERVER_PASSWORD = u'topsecret'
|
||||
response = self.dispatcher.handle_request(u'password "secret"')
|
||||
|
||||
self.sendRequest(u'password "secret"')
|
||||
self.assertFalse(self.dispatcher.authenticated)
|
||||
self.assert_(u'ACK [3@0] {password} incorrect password' in response)
|
||||
self.assertEqualResponse(u'ACK [3@0] {password} incorrect password')
|
||||
|
||||
def test_authentication_with_anything_when_password_check_turned_off(self):
|
||||
settings.MPD_SERVER_PASSWORD = None
|
||||
response = self.dispatcher.handle_request(u'any request at all')
|
||||
|
||||
self.sendRequest(u'any request at all')
|
||||
self.assertTrue(self.dispatcher.authenticated)
|
||||
self.assert_('ACK [5@0] {} unknown command "any"' in response)
|
||||
self.assertEqualResponse('ACK [5@0] {} unknown command "any"')
|
||||
|
||||
def test_anything_when_not_authenticated_should_fail(self):
|
||||
settings.MPD_SERVER_PASSWORD = u'topsecret'
|
||||
response = self.dispatcher.handle_request(u'any request at all')
|
||||
|
||||
self.sendRequest(u'any request at all')
|
||||
self.assertFalse(self.dispatcher.authenticated)
|
||||
self.assert_(
|
||||
u'ACK [4@0] {any} you don\'t have permission for "any"' in response)
|
||||
self.assertEqualResponse(
|
||||
u'ACK [4@0] {any} you don\'t have permission for "any"')
|
||||
|
||||
def test_close_is_allowed_without_authentication(self):
|
||||
settings.MPD_SERVER_PASSWORD = u'topsecret'
|
||||
response = self.dispatcher.handle_request(u'close')
|
||||
|
||||
self.sendRequest(u'close')
|
||||
self.assertFalse(self.dispatcher.authenticated)
|
||||
self.assert_(u'OK' in response)
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_commands_is_allowed_without_authentication(self):
|
||||
settings.MPD_SERVER_PASSWORD = u'topsecret'
|
||||
response = self.dispatcher.handle_request(u'commands')
|
||||
|
||||
self.sendRequest(u'commands')
|
||||
self.assertFalse(self.dispatcher.authenticated)
|
||||
self.assert_(u'OK' in response)
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_notcommands_is_allowed_without_authentication(self):
|
||||
settings.MPD_SERVER_PASSWORD = u'topsecret'
|
||||
response = self.dispatcher.handle_request(u'notcommands')
|
||||
|
||||
self.sendRequest(u'notcommands')
|
||||
self.assertFalse(self.dispatcher.authenticated)
|
||||
self.assert_(u'OK' in response)
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_ping_is_allowed_without_authentication(self):
|
||||
settings.MPD_SERVER_PASSWORD = u'topsecret'
|
||||
response = self.dispatcher.handle_request(u'ping')
|
||||
|
||||
self.sendRequest(u'ping')
|
||||
self.assertFalse(self.dispatcher.authenticated)
|
||||
self.assert_(u'OK' in response)
|
||||
self.assertInResponse(u'OK')
|
||||
53
tests/frontends/mpd/protocol/command_list_test.py
Normal file
53
tests/frontends/mpd/protocol/command_list_test.py
Normal file
@ -0,0 +1,53 @@
|
||||
from tests.frontends.mpd import protocol
|
||||
|
||||
class CommandListsTest(protocol.BaseTestCase):
|
||||
def test_command_list_begin(self):
|
||||
response = self.sendRequest(u'command_list_begin')
|
||||
self.assertEquals([], response)
|
||||
|
||||
def test_command_list_end(self):
|
||||
self.sendRequest(u'command_list_begin')
|
||||
self.sendRequest(u'command_list_end')
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_command_list_end_without_start_first_is_an_unknown_command(self):
|
||||
self.sendRequest(u'command_list_end')
|
||||
self.assertEqualResponse(
|
||||
u'ACK [5@0] {} unknown command "command_list_end"')
|
||||
|
||||
def test_command_list_with_ping(self):
|
||||
self.sendRequest(u'command_list_begin')
|
||||
self.assertEqual([], self.dispatcher.command_list)
|
||||
self.assertEqual(False, self.dispatcher.command_list_ok)
|
||||
self.sendRequest(u'ping')
|
||||
self.assert_(u'ping' in self.dispatcher.command_list)
|
||||
self.sendRequest(u'command_list_end')
|
||||
self.assertInResponse(u'OK')
|
||||
self.assertEqual(False, self.dispatcher.command_list)
|
||||
|
||||
def test_command_list_with_error_returns_ack_with_correct_index(self):
|
||||
self.sendRequest(u'command_list_begin')
|
||||
self.sendRequest(u'play') # Known command
|
||||
self.sendRequest(u'paly') # Unknown command
|
||||
self.sendRequest(u'command_list_end')
|
||||
self.assertEqualResponse(u'ACK [5@1] {} unknown command "paly"')
|
||||
|
||||
def test_command_list_ok_begin(self):
|
||||
response = self.sendRequest(u'command_list_ok_begin')
|
||||
self.assertEquals([], response)
|
||||
|
||||
def test_command_list_ok_with_ping(self):
|
||||
self.sendRequest(u'command_list_ok_begin')
|
||||
self.assertEqual([], self.dispatcher.command_list)
|
||||
self.assertEqual(True, self.dispatcher.command_list_ok)
|
||||
self.sendRequest(u'ping')
|
||||
self.assert_(u'ping' in self.dispatcher.command_list)
|
||||
self.sendRequest(u'command_list_end')
|
||||
self.assertInResponse(u'list_OK')
|
||||
self.assertInResponse(u'OK')
|
||||
self.assertEqual(False, self.dispatcher.command_list)
|
||||
self.assertEqual(False, self.dispatcher.command_list_ok)
|
||||
|
||||
# FIXME this should also include the special handling of idle within a
|
||||
# command list. That is that once a idle/noidle command is found inside a
|
||||
# commad list, the rest of the list seems to be ignored.
|
||||
43
tests/frontends/mpd/protocol/connection_test.py
Normal file
43
tests/frontends/mpd/protocol/connection_test.py
Normal file
@ -0,0 +1,43 @@
|
||||
from mock import patch
|
||||
|
||||
from mopidy import settings
|
||||
|
||||
from tests.frontends.mpd import protocol
|
||||
|
||||
class ConnectionHandlerTest(protocol.BaseTestCase):
|
||||
def test_close_closes_the_client_connection(self):
|
||||
with patch.object(self.session, 'close') as close_mock:
|
||||
response = self.sendRequest(u'close')
|
||||
close_mock.assertEqualResponsecalled_once_with()
|
||||
self.assertEqualResponse(u'OK')
|
||||
|
||||
def test_empty_request(self):
|
||||
self.sendRequest(u'')
|
||||
self.assertEqualResponse(u'OK')
|
||||
|
||||
self.sendRequest(u' ')
|
||||
self.assertEqualResponse(u'OK')
|
||||
|
||||
def test_kill(self):
|
||||
self.sendRequest(u'kill')
|
||||
self.assertEqualResponse(
|
||||
u'ACK [4@0] {kill} you don\'t have permission for "kill"')
|
||||
|
||||
def test_valid_password_is_accepted(self):
|
||||
settings.MPD_SERVER_PASSWORD = u'topsecret'
|
||||
self.sendRequest(u'password "topsecret"')
|
||||
self.assertEqualResponse(u'OK')
|
||||
|
||||
def test_invalid_password_is_not_accepted(self):
|
||||
settings.MPD_SERVER_PASSWORD = u'topsecret'
|
||||
self.sendRequest(u'password "secret"')
|
||||
self.assertEqualResponse(u'ACK [3@0] {password} incorrect password')
|
||||
|
||||
def test_any_password_is_not_accepted_when_password_check_turned_off(self):
|
||||
settings.MPD_SERVER_PASSWORD = None
|
||||
self.sendRequest(u'password "secret"')
|
||||
self.assertEqualResponse(u'ACK [3@0] {password} incorrect password')
|
||||
|
||||
def test_ping(self):
|
||||
self.sendRequest(u'ping')
|
||||
self.assertEqualResponse(u'OK')
|
||||
@ -1,20 +1,8 @@
|
||||
import unittest
|
||||
|
||||
from mopidy.backends.dummy import DummyBackend
|
||||
from mopidy.frontends.mpd.dispatcher import MpdDispatcher
|
||||
from mopidy.mixers.dummy import DummyMixer
|
||||
from mopidy.models import Track
|
||||
|
||||
class CurrentPlaylistHandlerTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.backend = DummyBackend.start().proxy()
|
||||
self.mixer = DummyMixer.start().proxy()
|
||||
self.dispatcher = MpdDispatcher()
|
||||
|
||||
def tearDown(self):
|
||||
self.backend.stop().get()
|
||||
self.mixer.stop().get()
|
||||
from tests.frontends.mpd import protocol
|
||||
|
||||
class CurrentPlaylistHandlerTest(protocol.BaseTestCase):
|
||||
def test_add(self):
|
||||
needle = Track(uri='dummy://foo')
|
||||
self.backend.library.provider.dummy_library = [
|
||||
@ -22,21 +10,21 @@ class CurrentPlaylistHandlerTest(unittest.TestCase):
|
||||
self.backend.current_playlist.append(
|
||||
[Track(), Track(), Track(), Track(), Track()])
|
||||
self.assertEqual(len(self.backend.current_playlist.tracks.get()), 5)
|
||||
result = self.dispatcher.handle_request(u'add "dummy://foo"')
|
||||
self.assertEqual(len(result), 1)
|
||||
self.assertEqual(result[0], u'OK')
|
||||
|
||||
self.sendRequest(u'add "dummy://foo"')
|
||||
self.assertEqual(len(self.backend.current_playlist.tracks.get()), 6)
|
||||
self.assertEqual(self.backend.current_playlist.tracks.get()[5], needle)
|
||||
self.assertEqualResponse(u'OK')
|
||||
|
||||
def test_add_with_uri_not_found_in_library_should_ack(self):
|
||||
result = self.dispatcher.handle_request(u'add "dummy://foo"')
|
||||
self.assertEqual(result[0],
|
||||
self.sendRequest(u'add "dummy://foo"')
|
||||
self.assertEqualResponse(
|
||||
u'ACK [50@0] {add} directory or file not found')
|
||||
|
||||
def test_add_with_empty_uri_should_add_all_known_tracks_and_ok(self):
|
||||
result = self.dispatcher.handle_request(u'add ""')
|
||||
self.sendRequest(u'add ""')
|
||||
# TODO check that we add all tracks (we currently don't)
|
||||
self.assert_(u'OK' in result)
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_addid_without_songpos(self):
|
||||
needle = Track(uri='dummy://foo')
|
||||
@ -45,16 +33,17 @@ class CurrentPlaylistHandlerTest(unittest.TestCase):
|
||||
self.backend.current_playlist.append(
|
||||
[Track(), Track(), Track(), Track(), Track()])
|
||||
self.assertEqual(len(self.backend.current_playlist.tracks.get()), 5)
|
||||
result = self.dispatcher.handle_request(u'addid "dummy://foo"')
|
||||
|
||||
self.sendRequest(u'addid "dummy://foo"')
|
||||
self.assertEqual(len(self.backend.current_playlist.tracks.get()), 6)
|
||||
self.assertEqual(self.backend.current_playlist.tracks.get()[5], needle)
|
||||
self.assert_(u'Id: %d' %
|
||||
self.backend.current_playlist.cp_tracks.get()[5][0] in result)
|
||||
self.assert_(u'OK' in result)
|
||||
self.assertInResponse(u'Id: %d' %
|
||||
self.backend.current_playlist.cp_tracks.get()[5][0])
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_addid_with_empty_uri_acks(self):
|
||||
result = self.dispatcher.handle_request(u'addid ""')
|
||||
self.assertEqual(result[0], u'ACK [50@0] {addid} No such song')
|
||||
self.sendRequest(u'addid ""')
|
||||
self.assertEqualResponse(u'ACK [50@0] {addid} No such song')
|
||||
|
||||
def test_addid_with_songpos(self):
|
||||
needle = Track(uri='dummy://foo')
|
||||
@ -63,12 +52,13 @@ class CurrentPlaylistHandlerTest(unittest.TestCase):
|
||||
self.backend.current_playlist.append(
|
||||
[Track(), Track(), Track(), Track(), Track()])
|
||||
self.assertEqual(len(self.backend.current_playlist.tracks.get()), 5)
|
||||
result = self.dispatcher.handle_request(u'addid "dummy://foo" "3"')
|
||||
|
||||
self.sendRequest(u'addid "dummy://foo" "3"')
|
||||
self.assertEqual(len(self.backend.current_playlist.tracks.get()), 6)
|
||||
self.assertEqual(self.backend.current_playlist.tracks.get()[3], needle)
|
||||
self.assert_(u'Id: %d' %
|
||||
self.backend.current_playlist.cp_tracks.get()[3][0] in result)
|
||||
self.assert_(u'OK' in result)
|
||||
self.assertInResponse(u'Id: %d' %
|
||||
self.backend.current_playlist.cp_tracks.get()[3][0])
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_addid_with_songpos_out_of_bounds_should_ack(self):
|
||||
needle = Track(uri='dummy://foo')
|
||||
@ -77,83 +67,93 @@ class CurrentPlaylistHandlerTest(unittest.TestCase):
|
||||
self.backend.current_playlist.append(
|
||||
[Track(), Track(), Track(), Track(), Track()])
|
||||
self.assertEqual(len(self.backend.current_playlist.tracks.get()), 5)
|
||||
result = self.dispatcher.handle_request(u'addid "dummy://foo" "6"')
|
||||
self.assertEqual(result[0], u'ACK [2@0] {addid} Bad song index')
|
||||
|
||||
self.sendRequest(u'addid "dummy://foo" "6"')
|
||||
self.assertEqualResponse(u'ACK [2@0] {addid} Bad song index')
|
||||
|
||||
def test_addid_with_uri_not_found_in_library_should_ack(self):
|
||||
result = self.dispatcher.handle_request(u'addid "dummy://foo"')
|
||||
self.assertEqual(result[0], u'ACK [50@0] {addid} No such song')
|
||||
self.sendRequest(u'addid "dummy://foo"')
|
||||
self.assertEqualResponse(u'ACK [50@0] {addid} No such song')
|
||||
|
||||
def test_clear(self):
|
||||
self.backend.current_playlist.append(
|
||||
[Track(), Track(), Track(), Track(), Track()])
|
||||
self.assertEqual(len(self.backend.current_playlist.tracks.get()), 5)
|
||||
result = self.dispatcher.handle_request(u'clear')
|
||||
|
||||
self.sendRequest(u'clear')
|
||||
self.assertEqual(len(self.backend.current_playlist.tracks.get()), 0)
|
||||
self.assertEqual(self.backend.playback.current_track.get(), None)
|
||||
self.assert_(u'OK' in result)
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_delete_songpos(self):
|
||||
self.backend.current_playlist.append(
|
||||
[Track(), Track(), Track(), Track(), Track()])
|
||||
self.assertEqual(len(self.backend.current_playlist.tracks.get()), 5)
|
||||
result = self.dispatcher.handle_request(u'delete "%d"' %
|
||||
|
||||
self.sendRequest(u'delete "%d"' %
|
||||
self.backend.current_playlist.cp_tracks.get()[2][0])
|
||||
self.assertEqual(len(self.backend.current_playlist.tracks.get()), 4)
|
||||
self.assert_(u'OK' in result)
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_delete_songpos_out_of_bounds(self):
|
||||
self.backend.current_playlist.append(
|
||||
[Track(), Track(), Track(), Track(), Track()])
|
||||
self.assertEqual(len(self.backend.current_playlist.tracks.get()), 5)
|
||||
result = self.dispatcher.handle_request(u'delete "5"')
|
||||
|
||||
self.sendRequest(u'delete "5"')
|
||||
self.assertEqual(len(self.backend.current_playlist.tracks.get()), 5)
|
||||
self.assertEqual(result[0], u'ACK [2@0] {delete} Bad song index')
|
||||
self.assertEqualResponse(u'ACK [2@0] {delete} Bad song index')
|
||||
|
||||
def test_delete_open_range(self):
|
||||
self.backend.current_playlist.append(
|
||||
[Track(), Track(), Track(), Track(), Track()])
|
||||
self.assertEqual(len(self.backend.current_playlist.tracks.get()), 5)
|
||||
result = self.dispatcher.handle_request(u'delete "1:"')
|
||||
|
||||
self.sendRequest(u'delete "1:"')
|
||||
self.assertEqual(len(self.backend.current_playlist.tracks.get()), 1)
|
||||
self.assert_(u'OK' in result)
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_delete_closed_range(self):
|
||||
self.backend.current_playlist.append(
|
||||
[Track(), Track(), Track(), Track(), Track()])
|
||||
self.assertEqual(len(self.backend.current_playlist.tracks.get()), 5)
|
||||
result = self.dispatcher.handle_request(u'delete "1:3"')
|
||||
|
||||
self.sendRequest(u'delete "1:3"')
|
||||
self.assertEqual(len(self.backend.current_playlist.tracks.get()), 3)
|
||||
self.assert_(u'OK' in result)
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_delete_range_out_of_bounds(self):
|
||||
self.backend.current_playlist.append(
|
||||
[Track(), Track(), Track(), Track(), Track()])
|
||||
self.assertEqual(len(self.backend.current_playlist.tracks.get()), 5)
|
||||
result = self.dispatcher.handle_request(u'delete "5:7"')
|
||||
|
||||
self.sendRequest(u'delete "5:7"')
|
||||
self.assertEqual(len(self.backend.current_playlist.tracks.get()), 5)
|
||||
self.assertEqual(result[0], u'ACK [2@0] {delete} Bad song index')
|
||||
self.assertEqualResponse(u'ACK [2@0] {delete} Bad song index')
|
||||
|
||||
def test_deleteid(self):
|
||||
self.backend.current_playlist.append([Track(), Track()])
|
||||
self.assertEqual(len(self.backend.current_playlist.tracks.get()), 2)
|
||||
result = self.dispatcher.handle_request(u'deleteid "1"')
|
||||
|
||||
self.sendRequest(u'deleteid "1"')
|
||||
self.assertEqual(len(self.backend.current_playlist.tracks.get()), 1)
|
||||
self.assert_(u'OK' in result)
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_deleteid_does_not_exist(self):
|
||||
self.backend.current_playlist.append([Track(), Track()])
|
||||
self.assertEqual(len(self.backend.current_playlist.tracks.get()), 2)
|
||||
result = self.dispatcher.handle_request(u'deleteid "12345"')
|
||||
|
||||
self.sendRequest(u'deleteid "12345"')
|
||||
self.assertEqual(len(self.backend.current_playlist.tracks.get()), 2)
|
||||
self.assertEqual(result[0], u'ACK [50@0] {deleteid} No such song')
|
||||
self.assertEqualResponse(u'ACK [50@0] {deleteid} No such song')
|
||||
|
||||
def test_move_songpos(self):
|
||||
self.backend.current_playlist.append([
|
||||
Track(name='a'), Track(name='b'), Track(name='c'),
|
||||
Track(name='d'), Track(name='e'), Track(name='f'),
|
||||
])
|
||||
result = self.dispatcher.handle_request(u'move "1" "0"')
|
||||
|
||||
self.sendRequest(u'move "1" "0"')
|
||||
tracks = self.backend.current_playlist.tracks.get()
|
||||
self.assertEqual(tracks[0].name, 'b')
|
||||
self.assertEqual(tracks[1].name, 'a')
|
||||
@ -161,14 +161,15 @@ class CurrentPlaylistHandlerTest(unittest.TestCase):
|
||||
self.assertEqual(tracks[3].name, 'd')
|
||||
self.assertEqual(tracks[4].name, 'e')
|
||||
self.assertEqual(tracks[5].name, 'f')
|
||||
self.assert_(u'OK' in result)
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_move_open_range(self):
|
||||
self.backend.current_playlist.append([
|
||||
Track(name='a'), Track(name='b'), Track(name='c'),
|
||||
Track(name='d'), Track(name='e'), Track(name='f'),
|
||||
])
|
||||
result = self.dispatcher.handle_request(u'move "2:" "0"')
|
||||
|
||||
self.sendRequest(u'move "2:" "0"')
|
||||
tracks = self.backend.current_playlist.tracks.get()
|
||||
self.assertEqual(tracks[0].name, 'c')
|
||||
self.assertEqual(tracks[1].name, 'd')
|
||||
@ -176,14 +177,15 @@ class CurrentPlaylistHandlerTest(unittest.TestCase):
|
||||
self.assertEqual(tracks[3].name, 'f')
|
||||
self.assertEqual(tracks[4].name, 'a')
|
||||
self.assertEqual(tracks[5].name, 'b')
|
||||
self.assert_(u'OK' in result)
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_move_closed_range(self):
|
||||
self.backend.current_playlist.append([
|
||||
Track(name='a'), Track(name='b'), Track(name='c'),
|
||||
Track(name='d'), Track(name='e'), Track(name='f'),
|
||||
])
|
||||
result = self.dispatcher.handle_request(u'move "1:3" "0"')
|
||||
|
||||
self.sendRequest(u'move "1:3" "0"')
|
||||
tracks = self.backend.current_playlist.tracks.get()
|
||||
self.assertEqual(tracks[0].name, 'b')
|
||||
self.assertEqual(tracks[1].name, 'c')
|
||||
@ -191,14 +193,15 @@ class CurrentPlaylistHandlerTest(unittest.TestCase):
|
||||
self.assertEqual(tracks[3].name, 'd')
|
||||
self.assertEqual(tracks[4].name, 'e')
|
||||
self.assertEqual(tracks[5].name, 'f')
|
||||
self.assert_(u'OK' in result)
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_moveid(self):
|
||||
self.backend.current_playlist.append([
|
||||
Track(name='a'), Track(name='b'), Track(name='c'),
|
||||
Track(name='d'), Track(name='e'), Track(name='f'),
|
||||
])
|
||||
result = self.dispatcher.handle_request(u'moveid "4" "2"')
|
||||
|
||||
self.sendRequest(u'moveid "4" "2"')
|
||||
tracks = self.backend.current_playlist.tracks.get()
|
||||
self.assertEqual(tracks[0].name, 'a')
|
||||
self.assertEqual(tracks[1].name, 'b')
|
||||
@ -206,179 +209,182 @@ class CurrentPlaylistHandlerTest(unittest.TestCase):
|
||||
self.assertEqual(tracks[3].name, 'c')
|
||||
self.assertEqual(tracks[4].name, 'd')
|
||||
self.assertEqual(tracks[5].name, 'f')
|
||||
self.assert_(u'OK' in result)
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_playlist_returns_same_as_playlistinfo(self):
|
||||
playlist_result = self.dispatcher.handle_request(u'playlist')
|
||||
playlistinfo_result = self.dispatcher.handle_request(u'playlistinfo')
|
||||
self.assertEqual(playlist_result, playlistinfo_result)
|
||||
playlist_response = self.sendRequest(u'playlist')
|
||||
playlistinfo_response = self.sendRequest(u'playlistinfo')
|
||||
self.assertEqual(playlist_response, playlistinfo_response)
|
||||
|
||||
def test_playlistfind(self):
|
||||
result = self.dispatcher.handle_request(u'playlistfind "tag" "needle"')
|
||||
self.assert_(u'ACK [0@0] {} Not implemented' in result)
|
||||
self.sendRequest(u'playlistfind "tag" "needle"')
|
||||
self.assertEqualResponse(u'ACK [0@0] {} Not implemented')
|
||||
|
||||
def test_playlistfind_by_filename_not_in_current_playlist(self):
|
||||
result = self.dispatcher.handle_request(
|
||||
u'playlistfind "filename" "file:///dev/null"')
|
||||
self.assertEqual(len(result), 1)
|
||||
self.assert_(u'OK' in result)
|
||||
self.sendRequest(u'playlistfind "filename" "file:///dev/null"')
|
||||
self.assertEqualResponse(u'OK')
|
||||
|
||||
def test_playlistfind_by_filename_without_quotes(self):
|
||||
result = self.dispatcher.handle_request(
|
||||
u'playlistfind filename "file:///dev/null"')
|
||||
self.assertEqual(len(result), 1)
|
||||
self.assert_(u'OK' in result)
|
||||
self.sendRequest(u'playlistfind filename "file:///dev/null"')
|
||||
self.assertEqualResponse(u'OK')
|
||||
|
||||
def test_playlistfind_by_filename_in_current_playlist(self):
|
||||
self.backend.current_playlist.append([
|
||||
Track(uri='file:///exists')])
|
||||
result = self.dispatcher.handle_request(
|
||||
u'playlistfind filename "file:///exists"')
|
||||
self.assert_(u'file: file:///exists' in result)
|
||||
self.assert_(u'Id: 0' in result)
|
||||
self.assert_(u'Pos: 0' in result)
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
self.sendRequest( u'playlistfind filename "file:///exists"')
|
||||
self.assertInResponse(u'file: file:///exists')
|
||||
self.assertInResponse(u'Id: 0')
|
||||
self.assertInResponse(u'Pos: 0')
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_playlistid_without_songid(self):
|
||||
self.backend.current_playlist.append([Track(name='a'), Track(name='b')])
|
||||
result = self.dispatcher.handle_request(u'playlistid')
|
||||
self.assert_(u'Title: a' in result)
|
||||
self.assert_(u'Title: b' in result)
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
self.sendRequest(u'playlistid')
|
||||
self.assertInResponse(u'Title: a')
|
||||
self.assertInResponse(u'Title: b')
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_playlistid_with_songid(self):
|
||||
self.backend.current_playlist.append([Track(name='a'), Track(name='b')])
|
||||
result = self.dispatcher.handle_request(u'playlistid "1"')
|
||||
self.assert_(u'Title: a' not in result)
|
||||
self.assert_(u'Id: 0' not in result)
|
||||
self.assert_(u'Title: b' in result)
|
||||
self.assert_(u'Id: 1' in result)
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
self.sendRequest(u'playlistid "1"')
|
||||
self.assertNotInResponse(u'Title: a')
|
||||
self.assertNotInResponse(u'Id: 0')
|
||||
self.assertInResponse(u'Title: b')
|
||||
self.assertInResponse(u'Id: 1')
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_playlistid_with_not_existing_songid_fails(self):
|
||||
self.backend.current_playlist.append([Track(name='a'), Track(name='b')])
|
||||
result = self.dispatcher.handle_request(u'playlistid "25"')
|
||||
self.assertEqual(result[0], u'ACK [50@0] {playlistid} No such song')
|
||||
|
||||
self.sendRequest(u'playlistid "25"')
|
||||
self.assertEqualResponse(u'ACK [50@0] {playlistid} No such song')
|
||||
|
||||
def test_playlistinfo_without_songpos_or_range(self):
|
||||
self.backend.current_playlist.append([
|
||||
Track(name='a'), Track(name='b'), Track(name='c'),
|
||||
Track(name='d'), Track(name='e'), Track(name='f'),
|
||||
])
|
||||
result = self.dispatcher.handle_request(u'playlistinfo')
|
||||
self.assert_(u'Title: a' in result)
|
||||
self.assert_(u'Title: b' in result)
|
||||
self.assert_(u'Title: c' in result)
|
||||
self.assert_(u'Title: d' in result)
|
||||
self.assert_(u'Title: e' in result)
|
||||
self.assert_(u'Title: f' in result)
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
self.sendRequest(u'playlistinfo')
|
||||
self.assertInResponse(u'Title: a')
|
||||
self.assertInResponse(u'Title: b')
|
||||
self.assertInResponse(u'Title: c')
|
||||
self.assertInResponse(u'Title: d')
|
||||
self.assertInResponse(u'Title: e')
|
||||
self.assertInResponse(u'Title: f')
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_playlistinfo_with_songpos(self):
|
||||
self.backend.current_playlist.append([
|
||||
Track(name='a'), Track(name='b'), Track(name='c'),
|
||||
Track(name='d'), Track(name='e'), Track(name='f'),
|
||||
])
|
||||
result = self.dispatcher.handle_request(u'playlistinfo "4"')
|
||||
self.assert_(u'Title: a' not in result)
|
||||
self.assert_(u'Title: b' not in result)
|
||||
self.assert_(u'Title: c' not in result)
|
||||
self.assert_(u'Title: d' not in result)
|
||||
self.assert_(u'Title: e' in result)
|
||||
self.assert_(u'Title: f' not in result)
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
self.sendRequest(u'playlistinfo "4"')
|
||||
self.assertNotInResponse(u'Title: a')
|
||||
self.assertNotInResponse(u'Title: b')
|
||||
self.assertNotInResponse(u'Title: c')
|
||||
self.assertNotInResponse(u'Title: d')
|
||||
self.assertInResponse(u'Title: e')
|
||||
self.assertNotInResponse(u'Title: f')
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_playlistinfo_with_negative_songpos_same_as_playlistinfo(self):
|
||||
result1 = self.dispatcher.handle_request(u'playlistinfo "-1"')
|
||||
result2 = self.dispatcher.handle_request(u'playlistinfo')
|
||||
self.assertEqual(result1, result2)
|
||||
response1 = self.sendRequest(u'playlistinfo "-1"')
|
||||
response2 = self.sendRequest(u'playlistinfo')
|
||||
self.assertEqual(response1, response2)
|
||||
|
||||
def test_playlistinfo_with_open_range(self):
|
||||
self.backend.current_playlist.append([
|
||||
Track(name='a'), Track(name='b'), Track(name='c'),
|
||||
Track(name='d'), Track(name='e'), Track(name='f'),
|
||||
])
|
||||
result = self.dispatcher.handle_request(u'playlistinfo "2:"')
|
||||
self.assert_(u'Title: a' not in result)
|
||||
self.assert_(u'Title: b' not in result)
|
||||
self.assert_(u'Title: c' in result)
|
||||
self.assert_(u'Title: d' in result)
|
||||
self.assert_(u'Title: e' in result)
|
||||
self.assert_(u'Title: f' in result)
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
self.sendRequest(u'playlistinfo "2:"')
|
||||
self.assertNotInResponse(u'Title: a')
|
||||
self.assertNotInResponse(u'Title: b')
|
||||
self.assertInResponse(u'Title: c')
|
||||
self.assertInResponse(u'Title: d')
|
||||
self.assertInResponse(u'Title: e')
|
||||
self.assertInResponse(u'Title: f')
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_playlistinfo_with_closed_range(self):
|
||||
self.backend.current_playlist.append([
|
||||
Track(name='a'), Track(name='b'), Track(name='c'),
|
||||
Track(name='d'), Track(name='e'), Track(name='f'),
|
||||
])
|
||||
result = self.dispatcher.handle_request(u'playlistinfo "2:4"')
|
||||
self.assert_(u'Title: a' not in result)
|
||||
self.assert_(u'Title: b' not in result)
|
||||
self.assert_(u'Title: c' in result)
|
||||
self.assert_(u'Title: d' in result)
|
||||
self.assert_(u'Title: e' not in result)
|
||||
self.assert_(u'Title: f' not in result)
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
self.sendRequest(u'playlistinfo "2:4"')
|
||||
self.assertNotInResponse(u'Title: a')
|
||||
self.assertNotInResponse(u'Title: b')
|
||||
self.assertInResponse(u'Title: c')
|
||||
self.assertInResponse(u'Title: d')
|
||||
self.assertNotInResponse(u'Title: e')
|
||||
self.assertNotInResponse(u'Title: f')
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_playlistinfo_with_too_high_start_of_range_returns_arg_error(self):
|
||||
result = self.dispatcher.handle_request(u'playlistinfo "10:20"')
|
||||
self.assert_(u'ACK [2@0] {playlistinfo} Bad song index' in result)
|
||||
self.sendRequest(u'playlistinfo "10:20"')
|
||||
self.assertEqualResponse(u'ACK [2@0] {playlistinfo} Bad song index')
|
||||
|
||||
def test_playlistinfo_with_too_high_end_of_range_returns_ok(self):
|
||||
result = self.dispatcher.handle_request(u'playlistinfo "0:20"')
|
||||
self.assert_(u'OK' in result)
|
||||
self.sendRequest(u'playlistinfo "0:20"')
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_playlistsearch(self):
|
||||
result = self.dispatcher.handle_request(
|
||||
u'playlistsearch "any" "needle"')
|
||||
self.assert_(u'ACK [0@0] {} Not implemented' in result)
|
||||
self.sendRequest( u'playlistsearch "any" "needle"')
|
||||
self.assertEqualResponse(u'ACK [0@0] {} Not implemented')
|
||||
|
||||
def test_playlistsearch_without_quotes(self):
|
||||
result = self.dispatcher.handle_request(u'playlistsearch any "needle"')
|
||||
self.assert_(u'ACK [0@0] {} Not implemented' in result)
|
||||
self.sendRequest(u'playlistsearch any "needle"')
|
||||
self.assertEqualResponse(u'ACK [0@0] {} Not implemented')
|
||||
|
||||
def test_plchanges(self):
|
||||
self.backend.current_playlist.append(
|
||||
[Track(name='a'), Track(name='b'), Track(name='c')])
|
||||
result = self.dispatcher.handle_request(u'plchanges "0"')
|
||||
self.assert_(u'Title: a' in result)
|
||||
self.assert_(u'Title: b' in result)
|
||||
self.assert_(u'Title: c' in result)
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
self.sendRequest(u'plchanges "0"')
|
||||
self.assertInResponse(u'Title: a')
|
||||
self.assertInResponse(u'Title: b')
|
||||
self.assertInResponse(u'Title: c')
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_plchanges_with_minus_one_returns_entire_playlist(self):
|
||||
self.backend.current_playlist.append(
|
||||
[Track(name='a'), Track(name='b'), Track(name='c')])
|
||||
result = self.dispatcher.handle_request(u'plchanges "-1"')
|
||||
self.assert_(u'Title: a' in result)
|
||||
self.assert_(u'Title: b' in result)
|
||||
self.assert_(u'Title: c' in result)
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
self.sendRequest(u'plchanges "-1"')
|
||||
self.assertInResponse(u'Title: a')
|
||||
self.assertInResponse(u'Title: b')
|
||||
self.assertInResponse(u'Title: c')
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_plchanges_without_quotes_works(self):
|
||||
self.backend.current_playlist.append(
|
||||
[Track(name='a'), Track(name='b'), Track(name='c')])
|
||||
result = self.dispatcher.handle_request(u'plchanges 0')
|
||||
self.assert_(u'Title: a' in result)
|
||||
self.assert_(u'Title: b' in result)
|
||||
self.assert_(u'Title: c' in result)
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
self.sendRequest(u'plchanges 0')
|
||||
self.assertInResponse(u'Title: a')
|
||||
self.assertInResponse(u'Title: b')
|
||||
self.assertInResponse(u'Title: c')
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_plchangesposid(self):
|
||||
self.backend.current_playlist.append([Track(), Track(), Track()])
|
||||
result = self.dispatcher.handle_request(u'plchangesposid "0"')
|
||||
|
||||
self.sendRequest(u'plchangesposid "0"')
|
||||
cp_tracks = self.backend.current_playlist.cp_tracks.get()
|
||||
self.assert_(u'cpos: 0' in result)
|
||||
self.assert_(u'Id: %d' % cp_tracks[0][0]
|
||||
in result)
|
||||
self.assert_(u'cpos: 2' in result)
|
||||
self.assert_(u'Id: %d' % cp_tracks[1][0]
|
||||
in result)
|
||||
self.assert_(u'cpos: 2' in result)
|
||||
self.assert_(u'Id: %d' % cp_tracks[2][0]
|
||||
in result)
|
||||
self.assert_(u'OK' in result)
|
||||
self.assertInResponse(u'cpos: 0')
|
||||
self.assertInResponse(u'Id: %d' % cp_tracks[0][0])
|
||||
self.assertInResponse(u'cpos: 2')
|
||||
self.assertInResponse(u'Id: %d' % cp_tracks[1][0])
|
||||
self.assertInResponse(u'cpos: 2')
|
||||
self.assertInResponse(u'Id: %d' % cp_tracks[2][0])
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_shuffle_without_range(self):
|
||||
self.backend.current_playlist.append([
|
||||
@ -386,9 +392,10 @@ class CurrentPlaylistHandlerTest(unittest.TestCase):
|
||||
Track(name='d'), Track(name='e'), Track(name='f'),
|
||||
])
|
||||
version = self.backend.current_playlist.version.get()
|
||||
result = self.dispatcher.handle_request(u'shuffle')
|
||||
|
||||
self.sendRequest(u'shuffle')
|
||||
self.assert_(version < self.backend.current_playlist.version.get())
|
||||
self.assert_(u'OK' in result)
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_shuffle_with_open_range(self):
|
||||
self.backend.current_playlist.append([
|
||||
@ -396,14 +403,15 @@ class CurrentPlaylistHandlerTest(unittest.TestCase):
|
||||
Track(name='d'), Track(name='e'), Track(name='f'),
|
||||
])
|
||||
version = self.backend.current_playlist.version.get()
|
||||
result = self.dispatcher.handle_request(u'shuffle "4:"')
|
||||
|
||||
self.sendRequest(u'shuffle "4:"')
|
||||
self.assert_(version < self.backend.current_playlist.version.get())
|
||||
tracks = self.backend.current_playlist.tracks.get()
|
||||
self.assertEqual(tracks[0].name, 'a')
|
||||
self.assertEqual(tracks[1].name, 'b')
|
||||
self.assertEqual(tracks[2].name, 'c')
|
||||
self.assertEqual(tracks[3].name, 'd')
|
||||
self.assert_(u'OK' in result)
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_shuffle_with_closed_range(self):
|
||||
self.backend.current_playlist.append([
|
||||
@ -411,21 +419,23 @@ class CurrentPlaylistHandlerTest(unittest.TestCase):
|
||||
Track(name='d'), Track(name='e'), Track(name='f'),
|
||||
])
|
||||
version = self.backend.current_playlist.version.get()
|
||||
result = self.dispatcher.handle_request(u'shuffle "1:3"')
|
||||
|
||||
self.sendRequest(u'shuffle "1:3"')
|
||||
self.assert_(version < self.backend.current_playlist.version.get())
|
||||
tracks = self.backend.current_playlist.tracks.get()
|
||||
self.assertEqual(tracks[0].name, 'a')
|
||||
self.assertEqual(tracks[3].name, 'd')
|
||||
self.assertEqual(tracks[4].name, 'e')
|
||||
self.assertEqual(tracks[5].name, 'f')
|
||||
self.assert_(u'OK' in result)
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_swap(self):
|
||||
self.backend.current_playlist.append([
|
||||
Track(name='a'), Track(name='b'), Track(name='c'),
|
||||
Track(name='d'), Track(name='e'), Track(name='f'),
|
||||
])
|
||||
result = self.dispatcher.handle_request(u'swap "1" "4"')
|
||||
|
||||
self.sendRequest(u'swap "1" "4"')
|
||||
tracks = self.backend.current_playlist.tracks.get()
|
||||
self.assertEqual(tracks[0].name, 'a')
|
||||
self.assertEqual(tracks[1].name, 'e')
|
||||
@ -433,14 +443,15 @@ class CurrentPlaylistHandlerTest(unittest.TestCase):
|
||||
self.assertEqual(tracks[3].name, 'd')
|
||||
self.assertEqual(tracks[4].name, 'b')
|
||||
self.assertEqual(tracks[5].name, 'f')
|
||||
self.assert_(u'OK' in result)
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_swapid(self):
|
||||
self.backend.current_playlist.append([
|
||||
Track(name='a'), Track(name='b'), Track(name='c'),
|
||||
Track(name='d'), Track(name='e'), Track(name='f'),
|
||||
])
|
||||
result = self.dispatcher.handle_request(u'swapid "1" "4"')
|
||||
|
||||
self.sendRequest(u'swapid "1" "4"')
|
||||
tracks = self.backend.current_playlist.tracks.get()
|
||||
self.assertEqual(tracks[0].name, 'a')
|
||||
self.assertEqual(tracks[1].name, 'e')
|
||||
@ -448,4 +459,4 @@ class CurrentPlaylistHandlerTest(unittest.TestCase):
|
||||
self.assertEqual(tracks[3].name, 'd')
|
||||
self.assertEqual(tracks[4].name, 'b')
|
||||
self.assertEqual(tracks[5].name, 'f')
|
||||
self.assert_(u'OK' in result)
|
||||
self.assertInResponse(u'OK')
|
||||
206
tests/frontends/mpd/protocol/idle_test.py
Normal file
206
tests/frontends/mpd/protocol/idle_test.py
Normal file
@ -0,0 +1,206 @@
|
||||
from mock import patch
|
||||
|
||||
from mopidy.frontends.mpd.protocol.status import SUBSYSTEMS
|
||||
from mopidy.models import Track
|
||||
|
||||
from tests.frontends.mpd import protocol
|
||||
|
||||
class IdleHandlerTest(protocol.BaseTestCase):
|
||||
def idleEvent(self, subsystem):
|
||||
self.session.on_idle(subsystem)
|
||||
|
||||
def assertEqualEvents(self, events):
|
||||
self.assertEqual(set(events), self.context.events)
|
||||
|
||||
def assertEqualSubscriptions(self, events):
|
||||
self.assertEqual(set(events), self.context.subscriptions)
|
||||
|
||||
def assertNoEvents(self):
|
||||
self.assertEqualEvents([])
|
||||
|
||||
def assertNoSubscriptions(self):
|
||||
self.assertEqualSubscriptions([])
|
||||
|
||||
def test_base_state(self):
|
||||
self.assertNoSubscriptions()
|
||||
self.assertNoEvents()
|
||||
self.assertNoResponse()
|
||||
|
||||
def test_idle(self):
|
||||
self.sendRequest(u'idle')
|
||||
self.assertEqualSubscriptions(SUBSYSTEMS)
|
||||
self.assertNoEvents()
|
||||
self.assertNoResponse()
|
||||
|
||||
def test_idle_disables_timeout(self):
|
||||
self.sendRequest(u'idle')
|
||||
self.connection.disable_timeout.assert_called_once_with()
|
||||
|
||||
def test_noidle(self):
|
||||
self.sendRequest(u'noidle')
|
||||
self.assertNoSubscriptions()
|
||||
self.assertNoEvents()
|
||||
self.assertNoResponse()
|
||||
|
||||
def test_idle_player(self):
|
||||
self.sendRequest(u'idle player')
|
||||
self.assertEqualSubscriptions(['player'])
|
||||
self.assertNoEvents()
|
||||
self.assertNoResponse()
|
||||
|
||||
def test_idle_player_playlist(self):
|
||||
self.sendRequest(u'idle player playlist')
|
||||
self.assertEqualSubscriptions(['player', 'playlist'])
|
||||
self.assertNoEvents()
|
||||
self.assertNoResponse()
|
||||
|
||||
def test_idle_then_noidle(self):
|
||||
self.sendRequest(u'idle')
|
||||
self.sendRequest(u'noidle')
|
||||
self.assertNoSubscriptions()
|
||||
self.assertNoEvents()
|
||||
self.assertOnceInResponse(u'OK')
|
||||
|
||||
def test_idle_then_noidle_enables_timeout(self):
|
||||
self.sendRequest(u'idle')
|
||||
self.sendRequest(u'noidle')
|
||||
self.connection.enable_timeout.assert_called_once_with()
|
||||
|
||||
def test_idle_then_play(self):
|
||||
with patch.object(self.session, 'stop') as stop_mock:
|
||||
self.sendRequest(u'idle')
|
||||
self.sendRequest(u'play')
|
||||
stop_mock.assert_called_once_with()
|
||||
|
||||
def test_idle_then_idle(self):
|
||||
with patch.object(self.session, 'stop') as stop_mock:
|
||||
self.sendRequest(u'idle')
|
||||
self.sendRequest(u'idle')
|
||||
stop_mock.assert_called_once_with()
|
||||
|
||||
def test_idle_player_then_play(self):
|
||||
with patch.object(self.session, 'stop') as stop_mock:
|
||||
self.sendRequest(u'idle player')
|
||||
self.sendRequest(u'play')
|
||||
stop_mock.assert_called_once_with()
|
||||
|
||||
def test_idle_then_player(self):
|
||||
self.sendRequest(u'idle')
|
||||
self.idleEvent(u'player')
|
||||
self.assertNoSubscriptions()
|
||||
self.assertNoEvents()
|
||||
self.assertOnceInResponse(u'changed: player')
|
||||
self.assertOnceInResponse(u'OK')
|
||||
|
||||
def test_idle_player_then_event_player(self):
|
||||
self.sendRequest(u'idle player')
|
||||
self.idleEvent(u'player')
|
||||
self.assertNoSubscriptions()
|
||||
self.assertNoEvents()
|
||||
self.assertOnceInResponse(u'changed: player')
|
||||
self.assertOnceInResponse(u'OK')
|
||||
|
||||
def test_idle_player_then_noidle(self):
|
||||
self.sendRequest(u'idle player')
|
||||
self.sendRequest(u'noidle')
|
||||
self.assertNoSubscriptions()
|
||||
self.assertNoEvents()
|
||||
self.assertOnceInResponse(u'OK')
|
||||
|
||||
def test_idle_player_playlist_then_noidle(self):
|
||||
self.sendRequest(u'idle player playlist')
|
||||
self.sendRequest(u'noidle')
|
||||
self.assertNoEvents()
|
||||
self.assertNoSubscriptions()
|
||||
self.assertOnceInResponse(u'OK')
|
||||
|
||||
def test_idle_player_playlist_then_player(self):
|
||||
self.sendRequest(u'idle player playlist')
|
||||
self.idleEvent(u'player')
|
||||
self.assertNoEvents()
|
||||
self.assertNoSubscriptions()
|
||||
self.assertOnceInResponse(u'changed: player')
|
||||
self.assertNotInResponse(u'changed: playlist')
|
||||
self.assertOnceInResponse(u'OK')
|
||||
|
||||
def test_idle_playlist_then_player(self):
|
||||
self.sendRequest(u'idle playlist')
|
||||
self.idleEvent(u'player')
|
||||
self.assertEqualEvents(['player'])
|
||||
self.assertEqualSubscriptions(['playlist'])
|
||||
self.assertNoResponse()
|
||||
|
||||
def test_idle_playlist_then_player_then_playlist(self):
|
||||
self.sendRequest(u'idle playlist')
|
||||
self.idleEvent(u'player')
|
||||
self.idleEvent(u'playlist')
|
||||
self.assertNoEvents()
|
||||
self.assertNoSubscriptions()
|
||||
self.assertNotInResponse(u'changed: player')
|
||||
self.assertOnceInResponse(u'changed: playlist')
|
||||
self.assertOnceInResponse(u'OK')
|
||||
|
||||
def test_player(self):
|
||||
self.idleEvent(u'player')
|
||||
self.assertEqualEvents(['player'])
|
||||
self.assertNoSubscriptions()
|
||||
self.assertNoResponse()
|
||||
|
||||
def test_player_then_idle_player(self):
|
||||
self.idleEvent(u'player')
|
||||
self.sendRequest(u'idle player')
|
||||
self.assertNoEvents()
|
||||
self.assertNoSubscriptions()
|
||||
self.assertOnceInResponse(u'changed: player')
|
||||
self.assertNotInResponse(u'changed: playlist')
|
||||
self.assertOnceInResponse(u'OK')
|
||||
|
||||
def test_player_then_playlist(self):
|
||||
self.idleEvent(u'player')
|
||||
self.idleEvent(u'playlist')
|
||||
self.assertEqualEvents(['player', 'playlist'])
|
||||
self.assertNoSubscriptions()
|
||||
self.assertNoResponse()
|
||||
|
||||
def test_player_then_idle(self):
|
||||
self.idleEvent(u'player')
|
||||
self.sendRequest(u'idle')
|
||||
self.assertNoEvents()
|
||||
self.assertNoSubscriptions()
|
||||
self.assertOnceInResponse(u'changed: player')
|
||||
self.assertOnceInResponse(u'OK')
|
||||
|
||||
def test_player_then_playlist_then_idle(self):
|
||||
self.idleEvent(u'player')
|
||||
self.idleEvent(u'playlist')
|
||||
self.sendRequest(u'idle')
|
||||
self.assertNoEvents()
|
||||
self.assertNoSubscriptions()
|
||||
self.assertOnceInResponse(u'changed: player')
|
||||
self.assertOnceInResponse(u'changed: playlist')
|
||||
self.assertOnceInResponse(u'OK')
|
||||
|
||||
def test_player_then_idle_playlist(self):
|
||||
self.idleEvent(u'player')
|
||||
self.sendRequest(u'idle playlist')
|
||||
self.assertEqualEvents(['player'])
|
||||
self.assertEqualSubscriptions(['playlist'])
|
||||
self.assertNoResponse()
|
||||
|
||||
def test_player_then_idle_playlist_then_noidle(self):
|
||||
self.idleEvent(u'player')
|
||||
self.sendRequest(u'idle playlist')
|
||||
self.sendRequest(u'noidle')
|
||||
self.assertNoEvents()
|
||||
self.assertNoSubscriptions()
|
||||
self.assertOnceInResponse(u'OK')
|
||||
|
||||
def test_player_then_playlist_then_idle_playlist(self):
|
||||
self.idleEvent(u'player')
|
||||
self.idleEvent(u'playlist')
|
||||
self.sendRequest(u'idle playlist')
|
||||
self.assertNoEvents()
|
||||
self.assertNoSubscriptions()
|
||||
self.assertNotInResponse(u'changed: player')
|
||||
self.assertOnceInResponse(u'changed: playlist')
|
||||
self.assertOnceInResponse(u'OK')
|
||||
343
tests/frontends/mpd/protocol/music_db_test.py
Normal file
343
tests/frontends/mpd/protocol/music_db_test.py
Normal file
@ -0,0 +1,343 @@
|
||||
from tests.frontends.mpd import protocol
|
||||
|
||||
class MusicDatabaseHandlerTest(protocol.BaseTestCase):
|
||||
def test_count(self):
|
||||
self.sendRequest(u'count "tag" "needle"')
|
||||
self.assertInResponse(u'songs: 0')
|
||||
self.assertInResponse(u'playtime: 0')
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_findadd(self):
|
||||
self.sendRequest(u'findadd "album" "what"')
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_listall(self):
|
||||
self.sendRequest(u'listall "file:///dev/urandom"')
|
||||
self.assertEqualResponse(u'ACK [0@0] {} Not implemented')
|
||||
|
||||
def test_listallinfo(self):
|
||||
self.sendRequest(u'listallinfo "file:///dev/urandom"')
|
||||
self.assertEqualResponse(u'ACK [0@0] {} Not implemented')
|
||||
|
||||
def test_lsinfo_without_path_returns_same_as_listplaylists(self):
|
||||
lsinfo_response = self.sendRequest(u'lsinfo')
|
||||
listplaylists_response = self.sendRequest(u'listplaylists')
|
||||
self.assertEqual(lsinfo_response, listplaylists_response)
|
||||
|
||||
def test_lsinfo_with_empty_path_returns_same_as_listplaylists(self):
|
||||
lsinfo_response = self.sendRequest(u'lsinfo ""')
|
||||
listplaylists_response = self.sendRequest(u'listplaylists')
|
||||
self.assertEqual(lsinfo_response, listplaylists_response)
|
||||
|
||||
def test_lsinfo_for_root_returns_same_as_listplaylists(self):
|
||||
lsinfo_response = self.sendRequest(u'lsinfo "/"')
|
||||
listplaylists_response = self.sendRequest(u'listplaylists')
|
||||
self.assertEqual(lsinfo_response, listplaylists_response)
|
||||
|
||||
def test_update_without_uri(self):
|
||||
self.sendRequest(u'update')
|
||||
self.assertInResponse(u'updating_db: 0')
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_update_with_uri(self):
|
||||
self.sendRequest(u'update "file:///dev/urandom"')
|
||||
self.assertInResponse(u'updating_db: 0')
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_rescan_without_uri(self):
|
||||
self.sendRequest(u'rescan')
|
||||
self.assertInResponse(u'updating_db: 0')
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_rescan_with_uri(self):
|
||||
self.sendRequest(u'rescan "file:///dev/urandom"')
|
||||
self.assertInResponse(u'updating_db: 0')
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
|
||||
class MusicDatabaseFindTest(protocol.BaseTestCase):
|
||||
def test_find_album(self):
|
||||
self.sendRequest(u'find "album" "what"')
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_find_album_without_quotes(self):
|
||||
self.sendRequest(u'find album "what"')
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_find_artist(self):
|
||||
self.sendRequest(u'find "artist" "what"')
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_find_artist_without_quotes(self):
|
||||
self.sendRequest(u'find artist "what"')
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_find_title(self):
|
||||
self.sendRequest(u'find "title" "what"')
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_find_title_without_quotes(self):
|
||||
self.sendRequest(u'find title "what"')
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_find_date(self):
|
||||
self.sendRequest(u'find "date" "2002-01-01"')
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_find_date_without_quotes(self):
|
||||
self.sendRequest(u'find date "2002-01-01"')
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_find_date_with_capital_d_and_incomplete_date(self):
|
||||
self.sendRequest(u'find Date "2005"')
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_find_else_should_fail(self):
|
||||
self.sendRequest(u'find "somethingelse" "what"')
|
||||
self.assertEqualResponse(u'ACK [2@0] {find} incorrect arguments')
|
||||
|
||||
def test_find_album_and_artist(self):
|
||||
self.sendRequest(u'find album "album_what" artist "artist_what"')
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
|
||||
class MusicDatabaseListTest(protocol.BaseTestCase):
|
||||
def test_list_foo_returns_ack(self):
|
||||
self.sendRequest(u'list "foo"')
|
||||
self.assertEqualResponse(u'ACK [2@0] {list} incorrect arguments')
|
||||
|
||||
### Artist
|
||||
|
||||
def test_list_artist_with_quotes(self):
|
||||
self.sendRequest(u'list "artist"')
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_list_artist_without_quotes(self):
|
||||
self.sendRequest(u'list artist')
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_list_artist_without_quotes_and_capitalized(self):
|
||||
self.sendRequest(u'list Artist')
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_list_artist_with_query_of_one_token(self):
|
||||
self.sendRequest(u'list "artist" "anartist"')
|
||||
self.assertEqualResponse(
|
||||
u'ACK [2@0] {list} should be "Album" for 3 arguments')
|
||||
|
||||
def test_list_artist_with_unknown_field_in_query_returns_ack(self):
|
||||
self.sendRequest(u'list "artist" "foo" "bar"')
|
||||
self.assertEqualResponse(u'ACK [2@0] {list} not able to parse args')
|
||||
|
||||
def test_list_artist_by_artist(self):
|
||||
self.sendRequest(u'list "artist" "artist" "anartist"')
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_list_artist_by_album(self):
|
||||
self.sendRequest(u'list "artist" "album" "analbum"')
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_list_artist_by_full_date(self):
|
||||
self.sendRequest(u'list "artist" "date" "2001-01-01"')
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_list_artist_by_year(self):
|
||||
self.sendRequest(u'list "artist" "date" "2001"')
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_list_artist_by_genre(self):
|
||||
self.sendRequest(u'list "artist" "genre" "agenre"')
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_list_artist_by_artist_and_album(self):
|
||||
self.sendRequest(
|
||||
u'list "artist" "artist" "anartist" "album" "analbum"')
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
### Album
|
||||
|
||||
def test_list_album_with_quotes(self):
|
||||
self.sendRequest(u'list "album"')
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_list_album_without_quotes(self):
|
||||
self.sendRequest(u'list album')
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_list_album_without_quotes_and_capitalized(self):
|
||||
self.sendRequest(u'list Album')
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_list_album_with_artist_name(self):
|
||||
self.sendRequest(u'list "album" "anartist"')
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_list_album_by_artist(self):
|
||||
self.sendRequest(u'list "album" "artist" "anartist"')
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_list_album_by_album(self):
|
||||
self.sendRequest(u'list "album" "album" "analbum"')
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_list_album_by_full_date(self):
|
||||
self.sendRequest(u'list "album" "date" "2001-01-01"')
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_list_album_by_year(self):
|
||||
self.sendRequest(u'list "album" "date" "2001"')
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_list_album_by_genre(self):
|
||||
self.sendRequest(u'list "album" "genre" "agenre"')
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_list_album_by_artist_and_album(self):
|
||||
self.sendRequest(
|
||||
u'list "album" "artist" "anartist" "album" "analbum"')
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
### Date
|
||||
|
||||
def test_list_date_with_quotes(self):
|
||||
self.sendRequest(u'list "date"')
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_list_date_without_quotes(self):
|
||||
self.sendRequest(u'list date')
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_list_date_without_quotes_and_capitalized(self):
|
||||
self.sendRequest(u'list Date')
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_list_date_with_query_of_one_token(self):
|
||||
self.sendRequest(u'list "date" "anartist"')
|
||||
self.assertEqualResponse(
|
||||
u'ACK [2@0] {list} should be "Album" for 3 arguments')
|
||||
|
||||
def test_list_date_by_artist(self):
|
||||
self.sendRequest(u'list "date" "artist" "anartist"')
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_list_date_by_album(self):
|
||||
self.sendRequest(u'list "date" "album" "analbum"')
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_list_date_by_full_date(self):
|
||||
self.sendRequest(u'list "date" "date" "2001-01-01"')
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_list_date_by_year(self):
|
||||
self.sendRequest(u'list "date" "date" "2001"')
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_list_date_by_genre(self):
|
||||
self.sendRequest(u'list "date" "genre" "agenre"')
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_list_date_by_artist_and_album(self):
|
||||
self.sendRequest(u'list "date" "artist" "anartist" "album" "analbum"')
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
### Genre
|
||||
|
||||
def test_list_genre_with_quotes(self):
|
||||
self.sendRequest(u'list "genre"')
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_list_genre_without_quotes(self):
|
||||
self.sendRequest(u'list genre')
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_list_genre_without_quotes_and_capitalized(self):
|
||||
self.sendRequest(u'list Genre')
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_list_genre_with_query_of_one_token(self):
|
||||
self.sendRequest(u'list "genre" "anartist"')
|
||||
self.assertEqualResponse(
|
||||
u'ACK [2@0] {list} should be "Album" for 3 arguments')
|
||||
|
||||
def test_list_genre_by_artist(self):
|
||||
self.sendRequest(u'list "genre" "artist" "anartist"')
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_list_genre_by_album(self):
|
||||
self.sendRequest(u'list "genre" "album" "analbum"')
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_list_genre_by_full_date(self):
|
||||
self.sendRequest(u'list "genre" "date" "2001-01-01"')
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_list_genre_by_year(self):
|
||||
self.sendRequest(u'list "genre" "date" "2001"')
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_list_genre_by_genre(self):
|
||||
self.sendRequest(u'list "genre" "genre" "agenre"')
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_list_genre_by_artist_and_album(self):
|
||||
self.sendRequest(
|
||||
u'list "genre" "artist" "anartist" "album" "analbum"')
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
|
||||
class MusicDatabaseSearchTest(protocol.BaseTestCase):
|
||||
def test_search_album(self):
|
||||
self.sendRequest(u'search "album" "analbum"')
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_search_album_without_quotes(self):
|
||||
self.sendRequest(u'search album "analbum"')
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_search_artist(self):
|
||||
self.sendRequest(u'search "artist" "anartist"')
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_search_artist_without_quotes(self):
|
||||
self.sendRequest(u'search artist "anartist"')
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_search_filename(self):
|
||||
self.sendRequest(u'search "filename" "afilename"')
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_search_filename_without_quotes(self):
|
||||
self.sendRequest(u'search filename "afilename"')
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_search_title(self):
|
||||
self.sendRequest(u'search "title" "atitle"')
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_search_title_without_quotes(self):
|
||||
self.sendRequest(u'search title "atitle"')
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_search_any(self):
|
||||
self.sendRequest(u'search "any" "anything"')
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_search_any_without_quotes(self):
|
||||
self.sendRequest(u'search any "anything"')
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_search_date(self):
|
||||
self.sendRequest(u'search "date" "2002-01-01"')
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_search_date_without_quotes(self):
|
||||
self.sendRequest(u'search date "2002-01-01"')
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_search_date_with_capital_d_and_incomplete_date(self):
|
||||
self.sendRequest(u'search Date "2005"')
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_search_else_should_fail(self):
|
||||
self.sendRequest(u'search "sometype" "something"')
|
||||
self.assertEqualResponse(u'ACK [2@0] {search} incorrect arguments')
|
||||
@ -1,163 +1,150 @@
|
||||
import unittest
|
||||
|
||||
from mopidy.backends.base import PlaybackController
|
||||
from mopidy.backends.dummy import DummyBackend
|
||||
from mopidy.frontends.mpd.dispatcher import MpdDispatcher
|
||||
from mopidy.mixers.dummy import DummyMixer
|
||||
from mopidy.backends import base as backend
|
||||
from mopidy.models import Track
|
||||
|
||||
from tests import SkipTest
|
||||
from tests.frontends.mpd import protocol
|
||||
|
||||
PAUSED = PlaybackController.PAUSED
|
||||
PLAYING = PlaybackController.PLAYING
|
||||
STOPPED = PlaybackController.STOPPED
|
||||
PAUSED = backend.PlaybackController.PAUSED
|
||||
PLAYING = backend.PlaybackController.PLAYING
|
||||
STOPPED = backend.PlaybackController.STOPPED
|
||||
|
||||
class PlaybackOptionsHandlerTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.backend = DummyBackend.start().proxy()
|
||||
self.mixer = DummyMixer.start().proxy()
|
||||
self.dispatcher = MpdDispatcher()
|
||||
|
||||
def tearDown(self):
|
||||
self.backend.stop().get()
|
||||
self.mixer.stop().get()
|
||||
|
||||
class PlaybackOptionsHandlerTest(protocol.BaseTestCase):
|
||||
def test_consume_off(self):
|
||||
result = self.dispatcher.handle_request(u'consume "0"')
|
||||
self.sendRequest(u'consume "0"')
|
||||
self.assertFalse(self.backend.playback.consume.get())
|
||||
self.assert_(u'OK' in result)
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_consume_off_without_quotes(self):
|
||||
result = self.dispatcher.handle_request(u'consume 0')
|
||||
self.sendRequest(u'consume 0')
|
||||
self.assertFalse(self.backend.playback.consume.get())
|
||||
self.assert_(u'OK' in result)
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_consume_on(self):
|
||||
result = self.dispatcher.handle_request(u'consume "1"')
|
||||
self.sendRequest(u'consume "1"')
|
||||
self.assertTrue(self.backend.playback.consume.get())
|
||||
self.assert_(u'OK' in result)
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_consume_on_without_quotes(self):
|
||||
result = self.dispatcher.handle_request(u'consume 1')
|
||||
self.sendRequest(u'consume 1')
|
||||
self.assertTrue(self.backend.playback.consume.get())
|
||||
self.assert_(u'OK' in result)
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_crossfade(self):
|
||||
result = self.dispatcher.handle_request(u'crossfade "10"')
|
||||
self.assert_(u'ACK [0@0] {} Not implemented' in result)
|
||||
self.sendRequest(u'crossfade "10"')
|
||||
self.assertInResponse(u'ACK [0@0] {} Not implemented')
|
||||
|
||||
def test_random_off(self):
|
||||
result = self.dispatcher.handle_request(u'random "0"')
|
||||
self.sendRequest(u'random "0"')
|
||||
self.assertFalse(self.backend.playback.random.get())
|
||||
self.assert_(u'OK' in result)
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_random_off_without_quotes(self):
|
||||
result = self.dispatcher.handle_request(u'random 0')
|
||||
self.sendRequest(u'random 0')
|
||||
self.assertFalse(self.backend.playback.random.get())
|
||||
self.assert_(u'OK' in result)
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_random_on(self):
|
||||
result = self.dispatcher.handle_request(u'random "1"')
|
||||
self.sendRequest(u'random "1"')
|
||||
self.assertTrue(self.backend.playback.random.get())
|
||||
self.assert_(u'OK' in result)
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_random_on_without_quotes(self):
|
||||
result = self.dispatcher.handle_request(u'random 1')
|
||||
self.sendRequest(u'random 1')
|
||||
self.assertTrue(self.backend.playback.random.get())
|
||||
self.assert_(u'OK' in result)
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_repeat_off(self):
|
||||
result = self.dispatcher.handle_request(u'repeat "0"')
|
||||
self.sendRequest(u'repeat "0"')
|
||||
self.assertFalse(self.backend.playback.repeat.get())
|
||||
self.assert_(u'OK' in result)
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_repeat_off_without_quotes(self):
|
||||
result = self.dispatcher.handle_request(u'repeat 0')
|
||||
self.sendRequest(u'repeat 0')
|
||||
self.assertFalse(self.backend.playback.repeat.get())
|
||||
self.assert_(u'OK' in result)
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_repeat_on(self):
|
||||
result = self.dispatcher.handle_request(u'repeat "1"')
|
||||
self.sendRequest(u'repeat "1"')
|
||||
self.assertTrue(self.backend.playback.repeat.get())
|
||||
self.assert_(u'OK' in result)
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_repeat_on_without_quotes(self):
|
||||
result = self.dispatcher.handle_request(u'repeat 1')
|
||||
self.sendRequest(u'repeat 1')
|
||||
self.assertTrue(self.backend.playback.repeat.get())
|
||||
self.assert_(u'OK' in result)
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_setvol_below_min(self):
|
||||
result = self.dispatcher.handle_request(u'setvol "-10"')
|
||||
self.assert_(u'OK' in result)
|
||||
self.sendRequest(u'setvol "-10"')
|
||||
self.assertEqual(0, self.mixer.volume.get())
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_setvol_min(self):
|
||||
result = self.dispatcher.handle_request(u'setvol "0"')
|
||||
self.assert_(u'OK' in result)
|
||||
self.sendRequest(u'setvol "0"')
|
||||
self.assertEqual(0, self.mixer.volume.get())
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_setvol_middle(self):
|
||||
result = self.dispatcher.handle_request(u'setvol "50"')
|
||||
self.assert_(u'OK' in result)
|
||||
self.sendRequest(u'setvol "50"')
|
||||
self.assertEqual(50, self.mixer.volume.get())
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_setvol_max(self):
|
||||
result = self.dispatcher.handle_request(u'setvol "100"')
|
||||
self.assert_(u'OK' in result)
|
||||
self.sendRequest(u'setvol "100"')
|
||||
self.assertEqual(100, self.mixer.volume.get())
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_setvol_above_max(self):
|
||||
result = self.dispatcher.handle_request(u'setvol "110"')
|
||||
self.assert_(u'OK' in result)
|
||||
self.sendRequest(u'setvol "110"')
|
||||
self.assertEqual(100, self.mixer.volume.get())
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_setvol_plus_is_ignored(self):
|
||||
result = self.dispatcher.handle_request(u'setvol "+10"')
|
||||
self.assert_(u'OK' in result)
|
||||
self.sendRequest(u'setvol "+10"')
|
||||
self.assertEqual(10, self.mixer.volume.get())
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_setvol_without_quotes(self):
|
||||
result = self.dispatcher.handle_request(u'setvol 50')
|
||||
self.assert_(u'OK' in result)
|
||||
self.sendRequest(u'setvol 50')
|
||||
self.assertEqual(50, self.mixer.volume.get())
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_single_off(self):
|
||||
result = self.dispatcher.handle_request(u'single "0"')
|
||||
self.sendRequest(u'single "0"')
|
||||
self.assertFalse(self.backend.playback.single.get())
|
||||
self.assert_(u'OK' in result)
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_single_off_without_quotes(self):
|
||||
result = self.dispatcher.handle_request(u'single 0')
|
||||
self.sendRequest(u'single 0')
|
||||
self.assertFalse(self.backend.playback.single.get())
|
||||
self.assert_(u'OK' in result)
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_single_on(self):
|
||||
result = self.dispatcher.handle_request(u'single "1"')
|
||||
self.sendRequest(u'single "1"')
|
||||
self.assertTrue(self.backend.playback.single.get())
|
||||
self.assert_(u'OK' in result)
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_single_on_without_quotes(self):
|
||||
result = self.dispatcher.handle_request(u'single 1')
|
||||
self.sendRequest(u'single 1')
|
||||
self.assertTrue(self.backend.playback.single.get())
|
||||
self.assert_(u'OK' in result)
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_replay_gain_mode_off(self):
|
||||
result = self.dispatcher.handle_request(u'replay_gain_mode "off"')
|
||||
self.assert_(u'ACK [0@0] {} Not implemented' in result)
|
||||
self.sendRequest(u'replay_gain_mode "off"')
|
||||
self.assertInResponse(u'ACK [0@0] {} Not implemented')
|
||||
|
||||
def test_replay_gain_mode_track(self):
|
||||
result = self.dispatcher.handle_request(u'replay_gain_mode "track"')
|
||||
self.assert_(u'ACK [0@0] {} Not implemented' in result)
|
||||
self.sendRequest(u'replay_gain_mode "track"')
|
||||
self.assertInResponse(u'ACK [0@0] {} Not implemented')
|
||||
|
||||
def test_replay_gain_mode_album(self):
|
||||
result = self.dispatcher.handle_request(u'replay_gain_mode "album"')
|
||||
self.assert_(u'ACK [0@0] {} Not implemented' in result)
|
||||
self.sendRequest(u'replay_gain_mode "album"')
|
||||
self.assertInResponse(u'ACK [0@0] {} Not implemented')
|
||||
|
||||
def test_replay_gain_status_default(self):
|
||||
expected = u'off'
|
||||
result = self.dispatcher.handle_request(u'replay_gain_status')
|
||||
self.assert_(u'OK' in result)
|
||||
self.assert_(expected in result)
|
||||
self.sendRequest(u'replay_gain_status')
|
||||
self.assertInResponse(u'OK')
|
||||
self.assertInResponse(u'off')
|
||||
|
||||
def test_replay_gain_status_off(self):
|
||||
raise SkipTest # TODO
|
||||
@ -169,79 +156,80 @@ class PlaybackOptionsHandlerTest(unittest.TestCase):
|
||||
raise SkipTest # TODO
|
||||
|
||||
|
||||
class PlaybackControlHandlerTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.backend = DummyBackend.start().proxy()
|
||||
self.mixer = DummyMixer.start().proxy()
|
||||
self.dispatcher = MpdDispatcher()
|
||||
|
||||
def tearDown(self):
|
||||
self.backend.stop().get()
|
||||
self.mixer.stop().get()
|
||||
|
||||
class PlaybackControlHandlerTest(protocol.BaseTestCase):
|
||||
def test_next(self):
|
||||
result = self.dispatcher.handle_request(u'next')
|
||||
self.assert_(u'OK' in result)
|
||||
self.sendRequest(u'next')
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_pause_off(self):
|
||||
self.backend.current_playlist.append([Track()])
|
||||
self.dispatcher.handle_request(u'play "0"')
|
||||
self.dispatcher.handle_request(u'pause "1"')
|
||||
result = self.dispatcher.handle_request(u'pause "0"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
self.sendRequest(u'play "0"')
|
||||
self.sendRequest(u'pause "1"')
|
||||
self.sendRequest(u'pause "0"')
|
||||
self.assertEqual(PLAYING, self.backend.playback.state.get())
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_pause_on(self):
|
||||
self.backend.current_playlist.append([Track()])
|
||||
self.dispatcher.handle_request(u'play "0"')
|
||||
result = self.dispatcher.handle_request(u'pause "1"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
self.sendRequest(u'play "0"')
|
||||
self.sendRequest(u'pause "1"')
|
||||
self.assertEqual(PAUSED, self.backend.playback.state.get())
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_pause_toggle(self):
|
||||
self.backend.current_playlist.append([Track()])
|
||||
result = self.dispatcher.handle_request(u'play "0"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
self.sendRequest(u'play "0"')
|
||||
self.assertEqual(PLAYING, self.backend.playback.state.get())
|
||||
result = self.dispatcher.handle_request(u'pause')
|
||||
self.assert_(u'OK' in result)
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
self.sendRequest(u'pause')
|
||||
self.assertEqual(PAUSED, self.backend.playback.state.get())
|
||||
result = self.dispatcher.handle_request(u'pause')
|
||||
self.assert_(u'OK' in result)
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
self.sendRequest(u'pause')
|
||||
self.assertEqual(PLAYING, self.backend.playback.state.get())
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_play_without_pos(self):
|
||||
self.backend.current_playlist.append([Track()])
|
||||
self.backend.playback.state = PAUSED
|
||||
result = self.dispatcher.handle_request(u'play')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
self.sendRequest(u'play')
|
||||
self.assertEqual(PLAYING, self.backend.playback.state.get())
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_play_with_pos(self):
|
||||
self.backend.current_playlist.append([Track()])
|
||||
result = self.dispatcher.handle_request(u'play "0"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
self.sendRequest(u'play "0"')
|
||||
self.assertEqual(PLAYING, self.backend.playback.state.get())
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_play_with_pos_without_quotes(self):
|
||||
self.backend.current_playlist.append([Track()])
|
||||
result = self.dispatcher.handle_request(u'play 0')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
self.sendRequest(u'play 0')
|
||||
self.assertEqual(PLAYING, self.backend.playback.state.get())
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_play_with_pos_out_of_bounds(self):
|
||||
self.backend.current_playlist.append([])
|
||||
result = self.dispatcher.handle_request(u'play "0"')
|
||||
self.assertEqual(result[0], u'ACK [2@0] {play} Bad song index')
|
||||
|
||||
self.sendRequest(u'play "0"')
|
||||
self.assertEqual(STOPPED, self.backend.playback.state.get())
|
||||
self.assertInResponse(u'ACK [2@0] {play} Bad song index')
|
||||
|
||||
def test_play_minus_one_plays_first_in_playlist_if_no_current_track(self):
|
||||
self.assertEqual(self.backend.playback.current_track.get(), None)
|
||||
self.backend.current_playlist.append([Track(uri='a'), Track(uri='b')])
|
||||
result = self.dispatcher.handle_request(u'play "-1"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
self.sendRequest(u'play "-1"')
|
||||
self.assertEqual(PLAYING, self.backend.playback.state.get())
|
||||
self.assertEqual(self.backend.playback.current_track.get().uri, 'a')
|
||||
self.assertEqual('a', self.backend.playback.current_track.get().uri)
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_play_minus_one_plays_current_track_if_current_track_is_set(self):
|
||||
self.backend.current_playlist.append([Track(uri='a'), Track(uri='b')])
|
||||
@ -250,27 +238,30 @@ class PlaybackControlHandlerTest(unittest.TestCase):
|
||||
self.backend.playback.next()
|
||||
self.backend.playback.stop()
|
||||
self.assertNotEqual(self.backend.playback.current_track.get(), None)
|
||||
result = self.dispatcher.handle_request(u'play "-1"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
self.sendRequest(u'play "-1"')
|
||||
self.assertEqual(PLAYING, self.backend.playback.state.get())
|
||||
self.assertEqual(self.backend.playback.current_track.get().uri, 'b')
|
||||
self.assertEqual('b', self.backend.playback.current_track.get().uri)
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_play_minus_one_on_empty_playlist_does_not_ack(self):
|
||||
self.backend.current_playlist.clear()
|
||||
result = self.dispatcher.handle_request(u'play "-1"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
self.sendRequest(u'play "-1"')
|
||||
self.assertEqual(STOPPED, self.backend.playback.state.get())
|
||||
self.assertEqual(self.backend.playback.current_track.get(), None)
|
||||
self.assertEqual(None, self.backend.playback.current_track.get())
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_play_minus_is_ignored_if_playing(self):
|
||||
self.backend.current_playlist.append([Track(length=40000)])
|
||||
self.backend.playback.seek(30000)
|
||||
self.assert_(self.backend.playback.time_position.get() >= 30000)
|
||||
self.assertEquals(PLAYING, self.backend.playback.state.get())
|
||||
result = self.dispatcher.handle_request(u'play "-1"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
self.sendRequest(u'play "-1"')
|
||||
self.assertEqual(PLAYING, self.backend.playback.state.get())
|
||||
self.assert_(self.backend.playback.time_position.get() >= 30000)
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_play_minus_one_resumes_if_paused(self):
|
||||
self.backend.current_playlist.append([Track(length=40000)])
|
||||
@ -279,24 +270,27 @@ class PlaybackControlHandlerTest(unittest.TestCase):
|
||||
self.assertEquals(PLAYING, self.backend.playback.state.get())
|
||||
self.backend.playback.pause()
|
||||
self.assertEquals(PAUSED, self.backend.playback.state.get())
|
||||
result = self.dispatcher.handle_request(u'play "-1"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
self.sendRequest(u'play "-1"')
|
||||
self.assertEqual(PLAYING, self.backend.playback.state.get())
|
||||
self.assert_(self.backend.playback.time_position.get() >= 30000)
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_playid(self):
|
||||
self.backend.current_playlist.append([Track()])
|
||||
result = self.dispatcher.handle_request(u'playid "0"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
self.sendRequest(u'playid "0"')
|
||||
self.assertEqual(PLAYING, self.backend.playback.state.get())
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_playid_minus_one_plays_first_in_playlist_if_no_current_track(self):
|
||||
self.assertEqual(self.backend.playback.current_track.get(), None)
|
||||
self.backend.current_playlist.append([Track(uri='a'), Track(uri='b')])
|
||||
result = self.dispatcher.handle_request(u'playid "-1"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
self.sendRequest(u'playid "-1"')
|
||||
self.assertEqual(PLAYING, self.backend.playback.state.get())
|
||||
self.assertEqual(self.backend.playback.current_track.get().uri, 'a')
|
||||
self.assertEqual('a', self.backend.playback.current_track.get().uri)
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_playid_minus_one_plays_current_track_if_current_track_is_set(self):
|
||||
self.backend.current_playlist.append([Track(uri='a'), Track(uri='b')])
|
||||
@ -304,28 +298,31 @@ class PlaybackControlHandlerTest(unittest.TestCase):
|
||||
self.backend.playback.play()
|
||||
self.backend.playback.next()
|
||||
self.backend.playback.stop()
|
||||
self.assertNotEqual(self.backend.playback.current_track.get(), None)
|
||||
result = self.dispatcher.handle_request(u'playid "-1"')
|
||||
self.assert_(u'OK' in result)
|
||||
self.assertNotEqual(None, self.backend.playback.current_track.get())
|
||||
|
||||
self.sendRequest(u'playid "-1"')
|
||||
self.assertEqual(PLAYING, self.backend.playback.state.get())
|
||||
self.assertEqual(self.backend.playback.current_track.get().uri, 'b')
|
||||
self.assertEqual('b', self.backend.playback.current_track.get().uri)
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_playid_minus_one_on_empty_playlist_does_not_ack(self):
|
||||
self.backend.current_playlist.clear()
|
||||
result = self.dispatcher.handle_request(u'playid "-1"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
self.sendRequest(u'playid "-1"')
|
||||
self.assertEqual(STOPPED, self.backend.playback.state.get())
|
||||
self.assertEqual(self.backend.playback.current_track.get(), None)
|
||||
self.assertEqual(None, self.backend.playback.current_track.get())
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_playid_minus_is_ignored_if_playing(self):
|
||||
self.backend.current_playlist.append([Track(length=40000)])
|
||||
self.backend.playback.seek(30000)
|
||||
self.assert_(self.backend.playback.time_position.get() >= 30000)
|
||||
self.assertEquals(PLAYING, self.backend.playback.state.get())
|
||||
result = self.dispatcher.handle_request(u'playid "-1"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
self.sendRequest(u'playid "-1"')
|
||||
self.assertEqual(PLAYING, self.backend.playback.state.get())
|
||||
self.assert_(self.backend.playback.time_position.get() >= 30000)
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_playid_minus_one_resumes_if_paused(self):
|
||||
self.backend.current_playlist.append([Track(length=40000)])
|
||||
@ -334,58 +331,64 @@ class PlaybackControlHandlerTest(unittest.TestCase):
|
||||
self.assertEquals(PLAYING, self.backend.playback.state.get())
|
||||
self.backend.playback.pause()
|
||||
self.assertEquals(PAUSED, self.backend.playback.state.get())
|
||||
result = self.dispatcher.handle_request(u'playid "-1"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
self.sendRequest(u'playid "-1"')
|
||||
self.assertEqual(PLAYING, self.backend.playback.state.get())
|
||||
self.assert_(self.backend.playback.time_position.get() >= 30000)
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_playid_which_does_not_exist(self):
|
||||
self.backend.current_playlist.append([Track()])
|
||||
result = self.dispatcher.handle_request(u'playid "12345"')
|
||||
self.assertEqual(result[0], u'ACK [50@0] {playid} No such song')
|
||||
|
||||
self.sendRequest(u'playid "12345"')
|
||||
self.assertInResponse(u'ACK [50@0] {playid} No such song')
|
||||
|
||||
def test_previous(self):
|
||||
result = self.dispatcher.handle_request(u'previous')
|
||||
self.assert_(u'OK' in result)
|
||||
self.sendRequest(u'previous')
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_seek(self):
|
||||
self.backend.current_playlist.append([Track(length=40000)])
|
||||
self.dispatcher.handle_request(u'seek "0"')
|
||||
result = self.dispatcher.handle_request(u'seek "0" "30"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
self.sendRequest(u'seek "0"')
|
||||
self.sendRequest(u'seek "0" "30"')
|
||||
self.assert_(self.backend.playback.time_position >= 30000)
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_seek_with_songpos(self):
|
||||
seek_track = Track(uri='2', length=40000)
|
||||
self.backend.current_playlist.append(
|
||||
[Track(uri='1', length=40000), seek_track])
|
||||
result = self.dispatcher.handle_request(u'seek "1" "30"')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
self.sendRequest(u'seek "1" "30"')
|
||||
self.assertEqual(self.backend.playback.current_track.get(), seek_track)
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_seek_without_quotes(self):
|
||||
self.backend.current_playlist.append([Track(length=40000)])
|
||||
self.dispatcher.handle_request(u'seek 0')
|
||||
result = self.dispatcher.handle_request(u'seek 0 30')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
self.sendRequest(u'seek 0')
|
||||
self.sendRequest(u'seek 0 30')
|
||||
self.assert_(self.backend.playback.time_position.get() >= 30000)
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_seekid(self):
|
||||
self.backend.current_playlist.append([Track(length=40000)])
|
||||
result = self.dispatcher.handle_request(u'seekid "0" "30"')
|
||||
self.assert_(u'OK' in result)
|
||||
self.sendRequest(u'seekid "0" "30"')
|
||||
self.assert_(self.backend.playback.time_position.get() >= 30000)
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_seekid_with_cpid(self):
|
||||
seek_track = Track(uri='2', length=40000)
|
||||
self.backend.current_playlist.append(
|
||||
[Track(length=40000), seek_track])
|
||||
result = self.dispatcher.handle_request(u'seekid "1" "30"')
|
||||
self.assert_(u'OK' in result)
|
||||
self.assertEqual(self.backend.playback.current_cpid.get(), 1)
|
||||
self.assertEqual(self.backend.playback.current_track.get(), seek_track)
|
||||
|
||||
self.sendRequest(u'seekid "1" "30"')
|
||||
self.assertEqual(1, self.backend.playback.current_cpid.get())
|
||||
self.assertEqual(seek_track, self.backend.playback.current_track.get())
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_stop(self):
|
||||
result = self.dispatcher.handle_request(u'stop')
|
||||
self.assert_(u'OK' in result)
|
||||
self.sendRequest(u'stop')
|
||||
self.assertEqual(STOPPED, self.backend.playback.state.get())
|
||||
self.assertInResponse(u'OK')
|
||||
66
tests/frontends/mpd/protocol/reflection_test.py
Normal file
66
tests/frontends/mpd/protocol/reflection_test.py
Normal file
@ -0,0 +1,66 @@
|
||||
from mopidy import settings
|
||||
|
||||
from tests.frontends.mpd import protocol
|
||||
|
||||
class ReflectionHandlerTest(protocol.BaseTestCase):
|
||||
def test_commands_returns_list_of_all_commands(self):
|
||||
self.sendRequest(u'commands')
|
||||
# Check if some random commands are included
|
||||
self.assertInResponse(u'command: commands')
|
||||
self.assertInResponse(u'command: play')
|
||||
self.assertInResponse(u'command: status')
|
||||
# Check if commands you do not have access to are not present
|
||||
self.assertNotInResponse(u'command: kill')
|
||||
# Check if the blacklisted commands are not present
|
||||
self.assertNotInResponse(u'command: command_list_begin')
|
||||
self.assertNotInResponse(u'command: command_list_ok_begin')
|
||||
self.assertNotInResponse(u'command: command_list_end')
|
||||
self.assertNotInResponse(u'command: idle')
|
||||
self.assertNotInResponse(u'command: noidle')
|
||||
self.assertNotInResponse(u'command: sticker')
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_commands_show_less_if_auth_required_and_not_authed(self):
|
||||
settings.MPD_SERVER_PASSWORD = u'secret'
|
||||
self.sendRequest(u'commands')
|
||||
# Not requiring auth
|
||||
self.assertInResponse(u'command: close')
|
||||
self.assertInResponse(u'command: commands')
|
||||
self.assertInResponse(u'command: notcommands')
|
||||
self.assertInResponse(u'command: password')
|
||||
self.assertInResponse(u'command: ping')
|
||||
# Requiring auth
|
||||
self.assertNotInResponse(u'command: play')
|
||||
self.assertNotInResponse(u'command: status')
|
||||
|
||||
def test_decoders(self):
|
||||
self.sendRequest(u'decoders')
|
||||
self.assertInResponse(u'ACK [0@0] {} Not implemented')
|
||||
|
||||
def test_notcommands_returns_only_kill_and_ok(self):
|
||||
response = self.sendRequest(u'notcommands')
|
||||
self.assertEqual(2, len(response))
|
||||
self.assertInResponse(u'command: kill')
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_notcommands_returns_more_if_auth_required_and_not_authed(self):
|
||||
settings.MPD_SERVER_PASSWORD = u'secret'
|
||||
self.sendRequest(u'notcommands')
|
||||
# Not requiring auth
|
||||
self.assertNotInResponse(u'command: close')
|
||||
self.assertNotInResponse(u'command: commands')
|
||||
self.assertNotInResponse(u'command: notcommands')
|
||||
self.assertNotInResponse(u'command: password')
|
||||
self.assertNotInResponse(u'command: ping')
|
||||
# Requiring auth
|
||||
self.assertInResponse(u'command: play')
|
||||
self.assertInResponse(u'command: status')
|
||||
|
||||
def test_tagtypes(self):
|
||||
self.sendRequest(u'tagtypes')
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_urlhandlers(self):
|
||||
self.sendRequest(u'urlhandlers')
|
||||
self.assertInResponse(u'OK')
|
||||
self.assertInResponse(u'handler: dummy')
|
||||
@ -1,12 +1,10 @@
|
||||
import random
|
||||
import unittest
|
||||
|
||||
from mopidy.backends.dummy import DummyBackend
|
||||
from mopidy.frontends.mpd import dispatcher
|
||||
from mopidy.mixers.dummy import DummyMixer
|
||||
from mopidy.models import Track
|
||||
|
||||
class IssueGH17RegressionTest(unittest.TestCase):
|
||||
from tests.frontends.mpd import protocol
|
||||
|
||||
class IssueGH17RegressionTest(protocol.BaseTestCase):
|
||||
"""
|
||||
The issue: http://github.com/mopidy/mopidy/issues#issue/17
|
||||
|
||||
@ -16,36 +14,27 @@ class IssueGH17RegressionTest(unittest.TestCase):
|
||||
- Turn on random mode
|
||||
- Press next until you get to the unplayable track
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
self.backend = DummyBackend.start().proxy()
|
||||
def test(self):
|
||||
self.backend.current_playlist.append([
|
||||
Track(uri='a'), Track(uri='b'), None,
|
||||
Track(uri='d'), Track(uri='e'), Track(uri='f')])
|
||||
self.mixer = DummyMixer.start().proxy()
|
||||
self.mpd = dispatcher.MpdDispatcher()
|
||||
|
||||
def tearDown(self):
|
||||
self.backend.stop().get()
|
||||
self.mixer.stop().get()
|
||||
|
||||
def test(self):
|
||||
random.seed(1) # Playlist order: abcfde
|
||||
self.mpd.handle_request(u'play')
|
||||
|
||||
self.sendRequest(u'play')
|
||||
self.assertEquals('a', self.backend.playback.current_track.get().uri)
|
||||
self.mpd.handle_request(u'random "1"')
|
||||
self.mpd.handle_request(u'next')
|
||||
self.sendRequest(u'random "1"')
|
||||
self.sendRequest(u'next')
|
||||
self.assertEquals('b', self.backend.playback.current_track.get().uri)
|
||||
self.mpd.handle_request(u'next')
|
||||
self.sendRequest(u'next')
|
||||
# Should now be at track 'c', but playback fails and it skips ahead
|
||||
self.assertEquals('f', self.backend.playback.current_track.get().uri)
|
||||
self.mpd.handle_request(u'next')
|
||||
self.sendRequest(u'next')
|
||||
self.assertEquals('d', self.backend.playback.current_track.get().uri)
|
||||
self.mpd.handle_request(u'next')
|
||||
self.sendRequest(u'next')
|
||||
self.assertEquals('e', self.backend.playback.current_track.get().uri)
|
||||
|
||||
|
||||
class IssueGH18RegressionTest(unittest.TestCase):
|
||||
class IssueGH18RegressionTest(protocol.BaseTestCase):
|
||||
"""
|
||||
The issue: http://github.com/mopidy/mopidy/issues#issue/18
|
||||
|
||||
@ -56,38 +45,30 @@ class IssueGH18RegressionTest(unittest.TestCase):
|
||||
At this point it gives the same song over and over.
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
self.backend = DummyBackend.start().proxy()
|
||||
def test(self):
|
||||
self.backend.current_playlist.append([
|
||||
Track(uri='a'), Track(uri='b'), Track(uri='c'),
|
||||
Track(uri='d'), Track(uri='e'), Track(uri='f')])
|
||||
self.mixer = DummyMixer.start().proxy()
|
||||
self.mpd = dispatcher.MpdDispatcher()
|
||||
|
||||
def tearDown(self):
|
||||
self.backend.stop().get()
|
||||
self.mixer.stop().get()
|
||||
|
||||
def test(self):
|
||||
random.seed(1)
|
||||
self.mpd.handle_request(u'play')
|
||||
self.mpd.handle_request(u'random "1"')
|
||||
self.mpd.handle_request(u'next')
|
||||
self.mpd.handle_request(u'random "0"')
|
||||
self.mpd.handle_request(u'next')
|
||||
|
||||
self.mpd.handle_request(u'next')
|
||||
self.sendRequest(u'play')
|
||||
self.sendRequest(u'random "1"')
|
||||
self.sendRequest(u'next')
|
||||
self.sendRequest(u'random "0"')
|
||||
self.sendRequest(u'next')
|
||||
|
||||
self.sendRequest(u'next')
|
||||
cp_track_1 = self.backend.playback.current_cp_track.get()
|
||||
self.mpd.handle_request(u'next')
|
||||
self.sendRequest(u'next')
|
||||
cp_track_2 = self.backend.playback.current_cp_track.get()
|
||||
self.mpd.handle_request(u'next')
|
||||
self.sendRequest(u'next')
|
||||
cp_track_3 = self.backend.playback.current_cp_track.get()
|
||||
|
||||
self.assertNotEqual(cp_track_1, cp_track_2)
|
||||
self.assertNotEqual(cp_track_2, cp_track_3)
|
||||
|
||||
|
||||
class IssueGH22RegressionTest(unittest.TestCase):
|
||||
class IssueGH22RegressionTest(protocol.BaseTestCase):
|
||||
"""
|
||||
The issue: http://github.com/mopidy/mopidy/issues/#issue/22
|
||||
|
||||
@ -100,32 +81,24 @@ class IssueGH22RegressionTest(unittest.TestCase):
|
||||
playlist, press next until it crashes.
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
self.backend = DummyBackend.start().proxy()
|
||||
def test(self):
|
||||
self.backend.current_playlist.append([
|
||||
Track(uri='a'), Track(uri='b'), Track(uri='c'),
|
||||
Track(uri='d'), Track(uri='e'), Track(uri='f')])
|
||||
self.mixer = DummyMixer.start().proxy()
|
||||
self.mpd = dispatcher.MpdDispatcher()
|
||||
|
||||
def tearDown(self):
|
||||
self.backend.stop().get()
|
||||
self.mixer.stop().get()
|
||||
|
||||
def test(self):
|
||||
random.seed(1)
|
||||
self.mpd.handle_request(u'play')
|
||||
self.mpd.handle_request(u'random "1"')
|
||||
self.mpd.handle_request(u'deleteid "1"')
|
||||
self.mpd.handle_request(u'deleteid "2"')
|
||||
self.mpd.handle_request(u'deleteid "3"')
|
||||
self.mpd.handle_request(u'deleteid "4"')
|
||||
self.mpd.handle_request(u'deleteid "5"')
|
||||
self.mpd.handle_request(u'deleteid "6"')
|
||||
self.mpd.handle_request(u'status')
|
||||
|
||||
self.sendRequest(u'play')
|
||||
self.sendRequest(u'random "1"')
|
||||
self.sendRequest(u'deleteid "1"')
|
||||
self.sendRequest(u'deleteid "2"')
|
||||
self.sendRequest(u'deleteid "3"')
|
||||
self.sendRequest(u'deleteid "4"')
|
||||
self.sendRequest(u'deleteid "5"')
|
||||
self.sendRequest(u'deleteid "6"')
|
||||
self.sendRequest(u'status')
|
||||
|
||||
|
||||
class IssueGH69RegressionTest(unittest.TestCase):
|
||||
class IssueGH69RegressionTest(protocol.BaseTestCase):
|
||||
"""
|
||||
The issue: https://github.com/mopidy/mopidy/issues#issue/69
|
||||
|
||||
@ -136,23 +109,14 @@ class IssueGH69RegressionTest(unittest.TestCase):
|
||||
The status response now contains "song: None".
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
self.backend = DummyBackend.start().proxy()
|
||||
def test(self):
|
||||
self.backend.stored_playlists.create('foo')
|
||||
self.backend.current_playlist.append([
|
||||
Track(uri='a'), Track(uri='b'), Track(uri='c'),
|
||||
Track(uri='d'), Track(uri='e'), Track(uri='f')])
|
||||
self.backend.stored_playlists.create('foo')
|
||||
self.mixer = DummyMixer.start().proxy()
|
||||
self.mpd = dispatcher.MpdDispatcher()
|
||||
|
||||
def tearDown(self):
|
||||
self.backend.stop().get()
|
||||
self.mixer.stop().get()
|
||||
|
||||
def test(self):
|
||||
self.mpd.handle_request(u'play')
|
||||
self.mpd.handle_request(u'stop')
|
||||
self.mpd.handle_request(u'clear')
|
||||
self.mpd.handle_request(u'load "foo"')
|
||||
response = self.mpd.handle_request(u'status')
|
||||
self.assert_('song: None' not in response)
|
||||
self.sendRequest(u'play')
|
||||
self.sendRequest(u'stop')
|
||||
self.sendRequest(u'clear')
|
||||
self.sendRequest(u'load "foo"')
|
||||
self.assertNotInResponse('song: None')
|
||||
36
tests/frontends/mpd/protocol/status_test.py
Normal file
36
tests/frontends/mpd/protocol/status_test.py
Normal file
@ -0,0 +1,36 @@
|
||||
from mopidy.models import Track
|
||||
|
||||
from tests.frontends.mpd import protocol
|
||||
|
||||
class StatusHandlerTest(protocol.BaseTestCase):
|
||||
def test_clearerror(self):
|
||||
self.sendRequest(u'clearerror')
|
||||
self.assertEqualResponse(u'ACK [0@0] {} Not implemented')
|
||||
|
||||
def test_currentsong(self):
|
||||
track = Track()
|
||||
self.backend.current_playlist.append([track])
|
||||
self.backend.playback.play()
|
||||
self.sendRequest(u'currentsong')
|
||||
self.assertInResponse(u'file: ')
|
||||
self.assertInResponse(u'Time: 0')
|
||||
self.assertInResponse(u'Artist: ')
|
||||
self.assertInResponse(u'Title: ')
|
||||
self.assertInResponse(u'Album: ')
|
||||
self.assertInResponse(u'Track: 0')
|
||||
self.assertInResponse(u'Date: ')
|
||||
self.assertInResponse(u'Pos: 0')
|
||||
self.assertInResponse(u'Id: 0')
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_currentsong_without_song(self):
|
||||
self.sendRequest(u'currentsong')
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_stats_command(self):
|
||||
self.sendRequest(u'stats')
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_status_command(self):
|
||||
self.sendRequest(u'status')
|
||||
self.assertInResponse(u'OK')
|
||||
32
tests/frontends/mpd/protocol/stickers_test.py
Normal file
32
tests/frontends/mpd/protocol/stickers_test.py
Normal file
@ -0,0 +1,32 @@
|
||||
from tests.frontends.mpd import protocol
|
||||
|
||||
class StickersHandlerTest(protocol.BaseTestCase):
|
||||
def test_sticker_get(self):
|
||||
self.sendRequest(
|
||||
u'sticker get "song" "file:///dev/urandom" "a_name"')
|
||||
self.assertEqualResponse(u'ACK [0@0] {} Not implemented')
|
||||
|
||||
def test_sticker_set(self):
|
||||
self.sendRequest(
|
||||
u'sticker set "song" "file:///dev/urandom" "a_name" "a_value"')
|
||||
self.assertEqualResponse(u'ACK [0@0] {} Not implemented')
|
||||
|
||||
def test_sticker_delete_with_name(self):
|
||||
self.sendRequest(
|
||||
u'sticker delete "song" "file:///dev/urandom" "a_name"')
|
||||
self.assertEqualResponse(u'ACK [0@0] {} Not implemented')
|
||||
|
||||
def test_sticker_delete_without_name(self):
|
||||
self.sendRequest(
|
||||
u'sticker delete "song" "file:///dev/urandom"')
|
||||
self.assertEqualResponse(u'ACK [0@0] {} Not implemented')
|
||||
|
||||
def test_sticker_list(self):
|
||||
self.sendRequest(
|
||||
u'sticker list "song" "file:///dev/urandom"')
|
||||
self.assertEqualResponse(u'ACK [0@0] {} Not implemented')
|
||||
|
||||
def test_sticker_find(self):
|
||||
self.sendRequest(
|
||||
u'sticker find "song" "file:///dev/urandom" "a_name"')
|
||||
self.assertEqualResponse(u'ACK [0@0] {} Not implemented')
|
||||
93
tests/frontends/mpd/protocol/stored_playlists_test.py
Normal file
93
tests/frontends/mpd/protocol/stored_playlists_test.py
Normal file
@ -0,0 +1,93 @@
|
||||
import datetime as dt
|
||||
|
||||
from mopidy.models import Track, Playlist
|
||||
|
||||
from tests.frontends.mpd import protocol
|
||||
|
||||
class StoredPlaylistsHandlerTest(protocol.BaseTestCase):
|
||||
def test_listplaylist(self):
|
||||
self.backend.stored_playlists.playlists = [
|
||||
Playlist(name='name', tracks=[Track(uri='file:///dev/urandom')])]
|
||||
|
||||
self.sendRequest(u'listplaylist "name"')
|
||||
self.assertInResponse(u'file: file:///dev/urandom')
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_listplaylist_fails_if_no_playlist_is_found(self):
|
||||
self.sendRequest(u'listplaylist "name"')
|
||||
self.assertEqualResponse(u'ACK [50@0] {listplaylist} No such playlist')
|
||||
|
||||
def test_listplaylistinfo(self):
|
||||
self.backend.stored_playlists.playlists = [
|
||||
Playlist(name='name', tracks=[Track(uri='file:///dev/urandom')])]
|
||||
|
||||
self.sendRequest(u'listplaylistinfo "name"')
|
||||
self.assertInResponse(u'file: file:///dev/urandom')
|
||||
self.assertInResponse(u'Track: 0')
|
||||
self.assertNotInResponse(u'Pos: 0')
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_listplaylistinfo_fails_if_no_playlist_is_found(self):
|
||||
self.sendRequest(u'listplaylistinfo "name"')
|
||||
self.assertEqualResponse(
|
||||
u'ACK [50@0] {listplaylistinfo} No such playlist')
|
||||
|
||||
def test_listplaylists(self):
|
||||
last_modified = dt.datetime(2001, 3, 17, 13, 41, 17, 12345)
|
||||
self.backend.stored_playlists.playlists = [Playlist(name='a',
|
||||
last_modified=last_modified)]
|
||||
|
||||
self.sendRequest(u'listplaylists')
|
||||
self.assertInResponse(u'playlist: a')
|
||||
# Date without microseconds and with time zone information
|
||||
self.assertInResponse(u'Last-Modified: 2001-03-17T13:41:17Z')
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_load_known_playlist_appends_to_current_playlist(self):
|
||||
self.backend.current_playlist.append([Track(uri='a'), Track(uri='b')])
|
||||
self.assertEqual(len(self.backend.current_playlist.tracks.get()), 2)
|
||||
self.backend.stored_playlists.playlists = [Playlist(name='A-list',
|
||||
tracks=[Track(uri='c'), Track(uri='d'), Track(uri='e')])]
|
||||
|
||||
self.sendRequest(u'load "A-list"')
|
||||
tracks = self.backend.current_playlist.tracks.get()
|
||||
self.assertEqual(5, len(tracks))
|
||||
self.assertEqual('a', tracks[0].uri)
|
||||
self.assertEqual('b', tracks[1].uri)
|
||||
self.assertEqual('c', tracks[2].uri)
|
||||
self.assertEqual('d', tracks[3].uri)
|
||||
self.assertEqual('e', tracks[4].uri)
|
||||
self.assertInResponse(u'OK')
|
||||
|
||||
def test_load_unknown_playlist_acks(self):
|
||||
self.sendRequest(u'load "unknown playlist"')
|
||||
self.assertEqual(0, len(self.backend.current_playlist.tracks.get()))
|
||||
self.assertEqualResponse(u'ACK [50@0] {load} No such playlist')
|
||||
|
||||
def test_playlistadd(self):
|
||||
self.sendRequest(u'playlistadd "name" "file:///dev/urandom"')
|
||||
self.assertEqualResponse(u'ACK [0@0] {} Not implemented')
|
||||
|
||||
def test_playlistclear(self):
|
||||
self.sendRequest(u'playlistclear "name"')
|
||||
self.assertEqualResponse(u'ACK [0@0] {} Not implemented')
|
||||
|
||||
def test_playlistdelete(self):
|
||||
self.sendRequest(u'playlistdelete "name" "5"')
|
||||
self.assertEqualResponse(u'ACK [0@0] {} Not implemented')
|
||||
|
||||
def test_playlistmove(self):
|
||||
self.sendRequest(u'playlistmove "name" "5" "10"')
|
||||
self.assertEqualResponse(u'ACK [0@0] {} Not implemented')
|
||||
|
||||
def test_rename(self):
|
||||
self.sendRequest(u'rename "old_name" "new_name"')
|
||||
self.assertEqualResponse(u'ACK [0@0] {} Not implemented')
|
||||
|
||||
def test_rm(self):
|
||||
self.sendRequest(u'rm "name"')
|
||||
self.assertEqualResponse(u'ACK [0@0] {} Not implemented')
|
||||
|
||||
def test_save(self):
|
||||
self.sendRequest(u'save "name"')
|
||||
self.assertEqualResponse(u'ACK [0@0] {} Not implemented')
|
||||
@ -1,79 +0,0 @@
|
||||
import unittest
|
||||
|
||||
from mopidy import settings
|
||||
from mopidy.backends.dummy import DummyBackend
|
||||
from mopidy.frontends.mpd.dispatcher import MpdDispatcher
|
||||
from mopidy.mixers.dummy import DummyMixer
|
||||
|
||||
class ReflectionHandlerTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.backend = DummyBackend.start().proxy()
|
||||
self.mixer = DummyMixer.start().proxy()
|
||||
self.dispatcher = MpdDispatcher()
|
||||
|
||||
def tearDown(self):
|
||||
settings.runtime.clear()
|
||||
self.backend.stop().get()
|
||||
self.mixer.stop().get()
|
||||
|
||||
def test_commands_returns_list_of_all_commands(self):
|
||||
result = self.dispatcher.handle_request(u'commands')
|
||||
# Check if some random commands are included
|
||||
self.assert_(u'command: commands' in result)
|
||||
self.assert_(u'command: play' in result)
|
||||
self.assert_(u'command: status' in result)
|
||||
# Check if commands you do not have access to are not present
|
||||
self.assert_(u'command: kill' not in result)
|
||||
# Check if the blacklisted commands are not present
|
||||
self.assert_(u'command: command_list_begin' not in result)
|
||||
self.assert_(u'command: command_list_ok_begin' not in result)
|
||||
self.assert_(u'command: command_list_end' not in result)
|
||||
self.assert_(u'command: idle' not in result)
|
||||
self.assert_(u'command: noidle' not in result)
|
||||
self.assert_(u'command: sticker' not in result)
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_commands_show_less_if_auth_required_and_not_authed(self):
|
||||
settings.MPD_SERVER_PASSWORD = u'secret'
|
||||
result = self.dispatcher.handle_request(u'commands')
|
||||
# Not requiring auth
|
||||
self.assert_(u'command: close' in result, result)
|
||||
self.assert_(u'command: commands' in result, result)
|
||||
self.assert_(u'command: notcommands' in result, result)
|
||||
self.assert_(u'command: password' in result, result)
|
||||
self.assert_(u'command: ping' in result, result)
|
||||
# Requiring auth
|
||||
self.assert_(u'command: play' not in result, result)
|
||||
self.assert_(u'command: status' not in result, result)
|
||||
|
||||
def test_decoders(self):
|
||||
result = self.dispatcher.handle_request(u'decoders')
|
||||
self.assert_(u'ACK [0@0] {} Not implemented' in result)
|
||||
|
||||
def test_notcommands_returns_only_kill_and_ok(self):
|
||||
result = self.dispatcher.handle_request(u'notcommands')
|
||||
self.assertEqual(2, len(result))
|
||||
self.assert_(u'command: kill' in result)
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_notcommands_returns_more_if_auth_required_and_not_authed(self):
|
||||
settings.MPD_SERVER_PASSWORD = u'secret'
|
||||
result = self.dispatcher.handle_request(u'notcommands')
|
||||
# Not requiring auth
|
||||
self.assert_(u'command: close' not in result, result)
|
||||
self.assert_(u'command: commands' not in result, result)
|
||||
self.assert_(u'command: notcommands' not in result, result)
|
||||
self.assert_(u'command: password' not in result, result)
|
||||
self.assert_(u'command: ping' not in result, result)
|
||||
# Requiring auth
|
||||
self.assert_(u'command: play' in result, result)
|
||||
self.assert_(u'command: status' in result, result)
|
||||
|
||||
def test_tagtypes(self):
|
||||
result = self.dispatcher.handle_request(u'tagtypes')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_urlhandlers(self):
|
||||
result = self.dispatcher.handle_request(u'urlhandlers')
|
||||
self.assert_(u'OK' in result)
|
||||
self.assert_(u'handler: dummy' in result)
|
||||
@ -1,67 +1,29 @@
|
||||
import unittest
|
||||
|
||||
from mopidy.backends.base import PlaybackController
|
||||
from mopidy.backends.dummy import DummyBackend
|
||||
from mopidy.frontends.mpd.dispatcher import MpdDispatcher
|
||||
from mopidy.backends import dummy as backend
|
||||
from mopidy.frontends.mpd import dispatcher
|
||||
from mopidy.frontends.mpd.protocol import status
|
||||
from mopidy.mixers.dummy import DummyMixer
|
||||
from mopidy.mixers import dummy as mixer
|
||||
from mopidy.models import Track
|
||||
|
||||
PAUSED = PlaybackController.PAUSED
|
||||
PLAYING = PlaybackController.PLAYING
|
||||
STOPPED = PlaybackController.STOPPED
|
||||
PAUSED = backend.PlaybackController.PAUSED
|
||||
PLAYING = backend.PlaybackController.PLAYING
|
||||
STOPPED = backend.PlaybackController.STOPPED
|
||||
|
||||
# FIXME migrate to using protocol.BaseTestCase instead of status.stats
|
||||
# directly?
|
||||
|
||||
class StatusHandlerTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.backend = DummyBackend.start().proxy()
|
||||
self.mixer = DummyMixer.start().proxy()
|
||||
self.dispatcher = MpdDispatcher()
|
||||
self.backend = backend.DummyBackend.start().proxy()
|
||||
self.mixer = mixer.DummyMixer.start().proxy()
|
||||
self.dispatcher = dispatcher.MpdDispatcher()
|
||||
self.context = self.dispatcher.context
|
||||
|
||||
def tearDown(self):
|
||||
self.backend.stop().get()
|
||||
self.mixer.stop().get()
|
||||
|
||||
def test_clearerror(self):
|
||||
result = self.dispatcher.handle_request(u'clearerror')
|
||||
self.assert_(u'ACK [0@0] {} Not implemented' in result)
|
||||
|
||||
def test_currentsong(self):
|
||||
track = Track()
|
||||
self.backend.current_playlist.append([track])
|
||||
self.backend.playback.play()
|
||||
result = self.dispatcher.handle_request(u'currentsong')
|
||||
self.assert_(u'file: ' in result)
|
||||
self.assert_(u'Time: 0' in result)
|
||||
self.assert_(u'Artist: ' in result)
|
||||
self.assert_(u'Title: ' in result)
|
||||
self.assert_(u'Album: ' in result)
|
||||
self.assert_(u'Track: 0' in result)
|
||||
self.assert_(u'Date: ' in result)
|
||||
self.assert_(u'Pos: 0' in result)
|
||||
self.assert_(u'Id: 0' in result)
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_currentsong_without_song(self):
|
||||
result = self.dispatcher.handle_request(u'currentsong')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_idle_without_subsystems(self):
|
||||
result = self.dispatcher.handle_request(u'idle')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_idle_with_subsystems(self):
|
||||
result = self.dispatcher.handle_request(u'idle database playlist')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_noidle(self):
|
||||
result = self.dispatcher.handle_request(u'noidle')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_stats_command(self):
|
||||
result = self.dispatcher.handle_request(u'stats')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_stats_method(self):
|
||||
result = status.stats(self.context)
|
||||
self.assert_('artists' in result)
|
||||
@ -79,10 +41,6 @@ class StatusHandlerTest(unittest.TestCase):
|
||||
self.assert_('playtime' in result)
|
||||
self.assert_(int(result['playtime']) >= 0)
|
||||
|
||||
def test_status_command(self):
|
||||
result = self.dispatcher.handle_request(u'status')
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_status_method_contains_volume_which_defaults_to_0(self):
|
||||
result = dict(status.status(self.context))
|
||||
self.assert_('volume' in result)
|
||||
@ -207,6 +165,13 @@ class StatusHandlerTest(unittest.TestCase):
|
||||
self.assert_('elapsed' in result)
|
||||
self.assertEqual(int(result['elapsed']), 59123)
|
||||
|
||||
def test_status_method_when_starting_playing_contains_elapsed_zero(self):
|
||||
self.backend.playback.state = PAUSED
|
||||
self.backend.playback.play_time_accumulated = 123 # Less than 1000ms
|
||||
result = dict(status.status(self.context))
|
||||
self.assert_('elapsed' in result)
|
||||
self.assertEqual(int(result['elapsed']), 0) # Zero
|
||||
|
||||
def test_status_method_when_playing_contains_bitrate(self):
|
||||
self.backend.current_playlist.append([Track(bitrate=320)])
|
||||
self.backend.playback.play()
|
||||
|
||||
@ -1,45 +0,0 @@
|
||||
import unittest
|
||||
|
||||
from mopidy.backends.dummy import DummyBackend
|
||||
from mopidy.frontends.mpd.dispatcher import MpdDispatcher
|
||||
from mopidy.mixers.dummy import DummyMixer
|
||||
|
||||
class StickersHandlerTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.backend = DummyBackend.start().proxy()
|
||||
self.mixer = DummyMixer.start().proxy()
|
||||
self.dispatcher = MpdDispatcher()
|
||||
|
||||
def tearDown(self):
|
||||
self.backend.stop().get()
|
||||
self.mixer.stop().get()
|
||||
|
||||
def test_sticker_get(self):
|
||||
result = self.dispatcher.handle_request(
|
||||
u'sticker get "song" "file:///dev/urandom" "a_name"')
|
||||
self.assert_(u'ACK [0@0] {} Not implemented' in result)
|
||||
|
||||
def test_sticker_set(self):
|
||||
result = self.dispatcher.handle_request(
|
||||
u'sticker set "song" "file:///dev/urandom" "a_name" "a_value"')
|
||||
self.assert_(u'ACK [0@0] {} Not implemented' in result)
|
||||
|
||||
def test_sticker_delete_with_name(self):
|
||||
result = self.dispatcher.handle_request(
|
||||
u'sticker delete "song" "file:///dev/urandom" "a_name"')
|
||||
self.assert_(u'ACK [0@0] {} Not implemented' in result)
|
||||
|
||||
def test_sticker_delete_without_name(self):
|
||||
result = self.dispatcher.handle_request(
|
||||
u'sticker delete "song" "file:///dev/urandom"')
|
||||
self.assert_(u'ACK [0@0] {} Not implemented' in result)
|
||||
|
||||
def test_sticker_list(self):
|
||||
result = self.dispatcher.handle_request(
|
||||
u'sticker list "song" "file:///dev/urandom"')
|
||||
self.assert_(u'ACK [0@0] {} Not implemented' in result)
|
||||
|
||||
def test_sticker_find(self):
|
||||
result = self.dispatcher.handle_request(
|
||||
u'sticker find "song" "file:///dev/urandom" "a_name"')
|
||||
self.assert_(u'ACK [0@0] {} Not implemented' in result)
|
||||
@ -1,102 +0,0 @@
|
||||
import datetime as dt
|
||||
import unittest
|
||||
|
||||
from mopidy.backends.dummy import DummyBackend
|
||||
from mopidy.frontends.mpd.dispatcher import MpdDispatcher
|
||||
from mopidy.mixers.dummy import DummyMixer
|
||||
from mopidy.models import Track, Playlist
|
||||
|
||||
class StoredPlaylistsHandlerTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.backend = DummyBackend.start().proxy()
|
||||
self.mixer = DummyMixer.start().proxy()
|
||||
self.dispatcher = MpdDispatcher()
|
||||
|
||||
def tearDown(self):
|
||||
self.backend.stop().get()
|
||||
self.mixer.stop().get()
|
||||
|
||||
def test_listplaylist(self):
|
||||
self.backend.stored_playlists.playlists = [
|
||||
Playlist(name='name', tracks=[Track(uri='file:///dev/urandom')])]
|
||||
result = self.dispatcher.handle_request(u'listplaylist "name"')
|
||||
self.assert_(u'file: file:///dev/urandom' in result)
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_listplaylist_fails_if_no_playlist_is_found(self):
|
||||
result = self.dispatcher.handle_request(u'listplaylist "name"')
|
||||
self.assertEqual(result[0],
|
||||
u'ACK [50@0] {listplaylist} No such playlist')
|
||||
|
||||
def test_listplaylistinfo(self):
|
||||
self.backend.stored_playlists.playlists = [
|
||||
Playlist(name='name', tracks=[Track(uri='file:///dev/urandom')])]
|
||||
result = self.dispatcher.handle_request(u'listplaylistinfo "name"')
|
||||
self.assert_(u'file: file:///dev/urandom' in result)
|
||||
self.assert_(u'Track: 0' in result)
|
||||
self.assert_(u'Pos: 0' not in result)
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_listplaylistinfo_fails_if_no_playlist_is_found(self):
|
||||
result = self.dispatcher.handle_request(u'listplaylistinfo "name"')
|
||||
self.assertEqual(result[0],
|
||||
u'ACK [50@0] {listplaylistinfo} No such playlist')
|
||||
|
||||
def test_listplaylists(self):
|
||||
last_modified = dt.datetime(2001, 3, 17, 13, 41, 17, 12345)
|
||||
self.backend.stored_playlists.playlists = [Playlist(name='a',
|
||||
last_modified=last_modified)]
|
||||
result = self.dispatcher.handle_request(u'listplaylists')
|
||||
self.assert_(u'playlist: a' in result)
|
||||
# Date without microseconds and with time zone information
|
||||
self.assert_(u'Last-Modified: 2001-03-17T13:41:17Z' in result)
|
||||
self.assert_(u'OK' in result)
|
||||
|
||||
def test_load_known_playlist_appends_to_current_playlist(self):
|
||||
self.backend.current_playlist.append([Track(uri='a'), Track(uri='b')])
|
||||
self.assertEqual(len(self.backend.current_playlist.tracks.get()), 2)
|
||||
self.backend.stored_playlists.playlists = [Playlist(name='A-list',
|
||||
tracks=[Track(uri='c'), Track(uri='d'), Track(uri='e')])]
|
||||
result = self.dispatcher.handle_request(u'load "A-list"')
|
||||
self.assert_(u'OK' in result)
|
||||
tracks = self.backend.current_playlist.tracks.get()
|
||||
self.assertEqual(len(tracks), 5)
|
||||
self.assertEqual(tracks[0].uri, 'a')
|
||||
self.assertEqual(tracks[1].uri, 'b')
|
||||
self.assertEqual(tracks[2].uri, 'c')
|
||||
self.assertEqual(tracks[3].uri, 'd')
|
||||
self.assertEqual(tracks[4].uri, 'e')
|
||||
|
||||
def test_load_unknown_playlist_acks(self):
|
||||
result = self.dispatcher.handle_request(u'load "unknown playlist"')
|
||||
self.assert_(u'ACK [50@0] {load} No such playlist' in result)
|
||||
self.assertEqual(len(self.backend.current_playlist.tracks.get()), 0)
|
||||
|
||||
def test_playlistadd(self):
|
||||
result = self.dispatcher.handle_request(
|
||||
u'playlistadd "name" "file:///dev/urandom"')
|
||||
self.assert_(u'ACK [0@0] {} Not implemented' in result)
|
||||
|
||||
def test_playlistclear(self):
|
||||
result = self.dispatcher.handle_request(u'playlistclear "name"')
|
||||
self.assert_(u'ACK [0@0] {} Not implemented' in result)
|
||||
|
||||
def test_playlistdelete(self):
|
||||
result = self.dispatcher.handle_request(u'playlistdelete "name" "5"')
|
||||
self.assert_(u'ACK [0@0] {} Not implemented' in result)
|
||||
|
||||
def test_playlistmove(self):
|
||||
result = self.dispatcher.handle_request(u'playlistmove "name" "5" "10"')
|
||||
self.assert_(u'ACK [0@0] {} Not implemented' in result)
|
||||
|
||||
def test_rename(self):
|
||||
result = self.dispatcher.handle_request(u'rename "old_name" "new_name"')
|
||||
self.assert_(u'ACK [0@0] {} Not implemented' in result)
|
||||
|
||||
def test_rm(self):
|
||||
result = self.dispatcher.handle_request(u'rm "name"')
|
||||
self.assert_(u'ACK [0@0] {} Not implemented' in result)
|
||||
|
||||
def test_save(self):
|
||||
result = self.dispatcher.handle_request(u'save "name"')
|
||||
self.assert_(u'ACK [0@0] {} Not implemented' in result)
|
||||
@ -7,14 +7,14 @@ class BackendListenerTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.listener = BackendListener()
|
||||
|
||||
def test_listener_has_default_impl_for_the_paused_playing_event(self):
|
||||
self.listener.paused_playing(Track(), 0)
|
||||
def test_listener_has_default_impl_for_the_track_playback_paused_event(self):
|
||||
self.listener.track_playback_paused(Track(), 0)
|
||||
|
||||
def test_listener_has_default_impl_for_the_resumed_playing_event(self):
|
||||
self.listener.resumed_playing(Track(), 0)
|
||||
def test_listener_has_default_impl_for_the_track_playback_resumed_event(self):
|
||||
self.listener.track_playback_resumed(Track(), 0)
|
||||
|
||||
def test_listener_has_default_impl_for_the_started_playing_event(self):
|
||||
self.listener.started_playing(Track())
|
||||
def test_listener_has_default_impl_for_the_track_playback_started(self):
|
||||
self.listener.track_playback_started(Track())
|
||||
|
||||
def test_listener_has_default_impl_for_the_stopped_playing_event(self):
|
||||
self.listener.stopped_playing(Track(), 0)
|
||||
def test_listener_has_default_impl_for_the_track_playback_ended(self):
|
||||
self.listener.track_playback_ended(Track(), 0)
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
#encoding: utf-8
|
||||
|
||||
import re
|
||||
import unittest
|
||||
|
||||
from mopidy.utils import network
|
||||
@ -9,13 +10,26 @@ from mock import sentinel, Mock
|
||||
class LineProtocolTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.mock = Mock(spec=network.LineProtocol)
|
||||
|
||||
self.mock.terminator = network.LineProtocol.terminator
|
||||
self.mock.encoding = network.LineProtocol.encoding
|
||||
self.mock.delimeter = network.LineProtocol.delimeter
|
||||
self.mock.prevent_timeout = False
|
||||
|
||||
def test_init_stores_values_in_attributes(self):
|
||||
delimeter = re.compile(network.LineProtocol.terminator)
|
||||
network.LineProtocol.__init__(self.mock, sentinel.connection)
|
||||
self.assertEqual(sentinel.connection, self.mock.connection)
|
||||
self.assertEqual('', self.mock.recv_buffer)
|
||||
self.assertEqual(delimeter, self.mock.delimeter)
|
||||
self.assertFalse(self.mock.prevent_timeout)
|
||||
|
||||
def test_init_compiles_delimeter(self):
|
||||
self.mock.delimeter = '\r?\n'
|
||||
delimeter = re.compile('\r?\n')
|
||||
|
||||
network.LineProtocol.__init__(self.mock, sentinel.connection)
|
||||
self.assertEqual(delimeter, self.mock.delimeter)
|
||||
|
||||
def test_on_receive_no_new_lines_adds_to_recv_buffer(self):
|
||||
self.mock.connection = Mock(spec=network.Connection)
|
||||
@ -36,6 +50,16 @@ class LineProtocolTest(unittest.TestCase):
|
||||
self.mock.connection.disable_timeout.assert_called_once_with()
|
||||
self.mock.connection.enable_timeout.assert_called_once_with()
|
||||
|
||||
def test_on_receive_toggles_unless_prevent_timeout_is_set(self):
|
||||
self.mock.connection = Mock(spec=network.Connection)
|
||||
self.mock.recv_buffer = ''
|
||||
self.mock.parse_lines.return_value = []
|
||||
self.mock.prevent_timeout = True
|
||||
|
||||
network.LineProtocol.on_receive(self.mock, {'received': 'data'})
|
||||
self.mock.connection.disable_timeout.assert_called_once_with()
|
||||
self.assertEqual(0, self.mock.connection.enable_timeout.call_count)
|
||||
|
||||
def test_on_receive_no_new_lines_calls_parse_lines(self):
|
||||
self.mock.connection = Mock(spec=network.Connection)
|
||||
self.mock.recv_buffer = ''
|
||||
@ -63,6 +87,15 @@ class LineProtocolTest(unittest.TestCase):
|
||||
network.LineProtocol.on_receive(self.mock, {'received': 'data\n'})
|
||||
self.mock.on_line_received.assert_called_once_with(sentinel.decoded)
|
||||
|
||||
def test_on_receive_with_new_line_with_failed_decode(self):
|
||||
self.mock.connection = Mock(spec=network.Connection)
|
||||
self.mock.recv_buffer = ''
|
||||
self.mock.parse_lines.return_value = [sentinel.line]
|
||||
self.mock.decode.return_value = None
|
||||
|
||||
network.LineProtocol.on_receive(self.mock, {'received': 'data\n'})
|
||||
self.assertEqual(0, self.mock.on_line_received.call_count)
|
||||
|
||||
def test_on_receive_with_new_lines_calls_on_recieve(self):
|
||||
self.mock.connection = Mock(spec=network.Connection)
|
||||
self.mock.recv_buffer = ''
|
||||
@ -74,18 +107,21 @@ class LineProtocolTest(unittest.TestCase):
|
||||
self.assertEqual(2, self.mock.on_line_received.call_count)
|
||||
|
||||
def test_parse_lines_emtpy_buffer(self):
|
||||
self.mock.delimeter = re.compile(r'\n')
|
||||
self.mock.recv_buffer = ''
|
||||
|
||||
lines = network.LineProtocol.parse_lines(self.mock)
|
||||
self.assertRaises(StopIteration, lines.next)
|
||||
|
||||
def test_parse_lines_no_terminator(self):
|
||||
self.mock.delimeter = re.compile(r'\n')
|
||||
self.mock.recv_buffer = 'data'
|
||||
|
||||
lines = network.LineProtocol.parse_lines(self.mock)
|
||||
self.assertRaises(StopIteration, lines.next)
|
||||
|
||||
def test_parse_lines_termintor(self):
|
||||
self.mock.delimeter = re.compile(r'\n')
|
||||
self.mock.recv_buffer = 'data\n'
|
||||
|
||||
lines = network.LineProtocol.parse_lines(self.mock)
|
||||
@ -93,7 +129,17 @@ class LineProtocolTest(unittest.TestCase):
|
||||
self.assertRaises(StopIteration, lines.next)
|
||||
self.assertEqual('', self.mock.recv_buffer)
|
||||
|
||||
def test_parse_lines_termintor_with_carriage_return(self):
|
||||
self.mock.delimeter = re.compile(r'\r?\n')
|
||||
self.mock.recv_buffer = 'data\r\n'
|
||||
|
||||
lines = network.LineProtocol.parse_lines(self.mock)
|
||||
self.assertEqual('data', lines.next())
|
||||
self.assertRaises(StopIteration, lines.next)
|
||||
self.assertEqual('', self.mock.recv_buffer)
|
||||
|
||||
def test_parse_lines_no_data_before_terminator(self):
|
||||
self.mock.delimeter = re.compile(r'\n')
|
||||
self.mock.recv_buffer = '\n'
|
||||
|
||||
lines = network.LineProtocol.parse_lines(self.mock)
|
||||
@ -102,6 +148,7 @@ class LineProtocolTest(unittest.TestCase):
|
||||
self.assertEqual('', self.mock.recv_buffer)
|
||||
|
||||
def test_parse_lines_extra_data_after_terminator(self):
|
||||
self.mock.delimeter = re.compile(r'\n')
|
||||
self.mock.recv_buffer = 'data1\ndata2'
|
||||
|
||||
lines = network.LineProtocol.parse_lines(self.mock)
|
||||
@ -110,6 +157,7 @@ class LineProtocolTest(unittest.TestCase):
|
||||
self.assertEqual('data2', self.mock.recv_buffer)
|
||||
|
||||
def test_parse_lines_unicode(self):
|
||||
self.mock.delimeter = re.compile(r'\n')
|
||||
self.mock.recv_buffer = u'æøå\n'.encode('utf-8')
|
||||
|
||||
lines = network.LineProtocol.parse_lines(self.mock)
|
||||
@ -118,6 +166,7 @@ class LineProtocolTest(unittest.TestCase):
|
||||
self.assertEqual('', self.mock.recv_buffer)
|
||||
|
||||
def test_parse_lines_multiple_lines(self):
|
||||
self.mock.delimeter = re.compile(r'\n')
|
||||
self.mock.recv_buffer = 'abc\ndef\nghi\njkl'
|
||||
|
||||
lines = network.LineProtocol.parse_lines(self.mock)
|
||||
@ -128,6 +177,7 @@ class LineProtocolTest(unittest.TestCase):
|
||||
self.assertEqual('jkl', self.mock.recv_buffer)
|
||||
|
||||
def test_parse_lines_multiple_calls(self):
|
||||
self.mock.delimeter = re.compile(r'\n')
|
||||
self.mock.recv_buffer = 'data1'
|
||||
|
||||
lines = network.LineProtocol.parse_lines(self.mock)
|
||||
|
||||
201
tools/idle.py
Normal file
201
tools/idle.py
Normal file
@ -0,0 +1,201 @@
|
||||
#! /usr/bin/env python
|
||||
|
||||
# This script is helper to systematicly test the behaviour of MPD's idle
|
||||
# command. It is simply provided as a quick hack, expect nothing more.
|
||||
|
||||
import logging
|
||||
import pprint
|
||||
import socket
|
||||
|
||||
host = ''
|
||||
port = 6601
|
||||
|
||||
url = "13 - a-ha - White Canvas.mp3"
|
||||
artist = "a-ha"
|
||||
|
||||
data = {'id': None, 'id2': None, 'url': url, 'artist': artist}
|
||||
|
||||
# Commands to run before test requests to coerce MPD into right state
|
||||
setup_requests = [
|
||||
'clear',
|
||||
'add "%(url)s"',
|
||||
'add "%(url)s"',
|
||||
'add "%(url)s"',
|
||||
'play',
|
||||
# 'pause', # Uncomment to test paused idle behaviour
|
||||
# 'stop', # Uncomment to test stopped idle behaviour
|
||||
]
|
||||
|
||||
# List of commands to test for idle behaviour. Ordering of list is important in
|
||||
# order to keep MPD state as intended. Commands that are obviously
|
||||
# informational only or "harmfull" have been excluded.
|
||||
test_requests = [
|
||||
'add "%(url)s"',
|
||||
'addid "%(url)s" "1"',
|
||||
'clear',
|
||||
# 'clearerror',
|
||||
# 'close',
|
||||
# 'commands',
|
||||
'consume "1"',
|
||||
'consume "0"',
|
||||
# 'count',
|
||||
'crossfade "1"',
|
||||
'crossfade "0"',
|
||||
# 'currentsong',
|
||||
# 'delete "1:2"',
|
||||
'delete "0"',
|
||||
'deleteid "%(id)s"',
|
||||
'disableoutput "0"',
|
||||
'enableoutput "0"',
|
||||
# 'find',
|
||||
# 'findadd "artist" "%(artist)s"',
|
||||
# 'idle',
|
||||
# 'kill',
|
||||
# 'list',
|
||||
# 'listall',
|
||||
# 'listallinfo',
|
||||
# 'listplaylist',
|
||||
# 'listplaylistinfo',
|
||||
# 'listplaylists',
|
||||
# 'lsinfo',
|
||||
'move "0:1" "2"',
|
||||
'move "0" "1"',
|
||||
'moveid "%(id)s" "1"',
|
||||
'next',
|
||||
# 'notcommands',
|
||||
# 'outputs',
|
||||
# 'password',
|
||||
'pause',
|
||||
# 'ping',
|
||||
'play',
|
||||
'playid "%(id)s"',
|
||||
# 'playlist',
|
||||
'playlistadd "foo" "%(url)s"',
|
||||
'playlistclear "foo"',
|
||||
'playlistadd "foo" "%(url)s"',
|
||||
'playlistdelete "foo" "0"',
|
||||
# 'playlistfind',
|
||||
# 'playlistid',
|
||||
# 'playlistinfo',
|
||||
'playlistadd "foo" "%(url)s"',
|
||||
'playlistadd "foo" "%(url)s"',
|
||||
'playlistmove "foo" "0" "1"',
|
||||
# 'playlistsearch',
|
||||
# 'plchanges',
|
||||
# 'plchangesposid',
|
||||
'previous',
|
||||
'random "1"',
|
||||
'random "0"',
|
||||
'rm "bar"',
|
||||
'rename "foo" "bar"',
|
||||
'repeat "0"',
|
||||
'rm "bar"',
|
||||
'save "bar"',
|
||||
'load "bar"',
|
||||
# 'search',
|
||||
'seek "1" "10"',
|
||||
'seekid "%(id)s" "10"',
|
||||
# 'setvol "10"',
|
||||
'shuffle',
|
||||
'shuffle "0:1"',
|
||||
'single "1"',
|
||||
'single "0"',
|
||||
# 'stats',
|
||||
# 'status',
|
||||
'stop',
|
||||
'swap "1" "2"',
|
||||
'swapid "%(id)s" "%(id2)s"',
|
||||
# 'tagtypes',
|
||||
# 'update',
|
||||
# 'urlhandlers',
|
||||
# 'volume',
|
||||
]
|
||||
|
||||
|
||||
def create_socketfile():
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
sock.connect((host, port))
|
||||
sock.settimeout(0.5)
|
||||
fd = sock.makefile('rw', 1) # 1 = line buffered
|
||||
fd.readline() # Read banner
|
||||
return fd
|
||||
|
||||
|
||||
def wait(fd, prefix=None, collect=None):
|
||||
while True:
|
||||
line = fd.readline().rstrip()
|
||||
if prefix:
|
||||
logging.debug('%s: %s', prefix, repr(line))
|
||||
if line.split()[0] in ('OK', 'ACK'):
|
||||
break
|
||||
|
||||
|
||||
def collect_ids(fd):
|
||||
fd.write('playlistinfo\n')
|
||||
|
||||
ids = []
|
||||
while True:
|
||||
line = fd.readline()
|
||||
if line.split()[0] == 'OK':
|
||||
break
|
||||
if line.split()[0] == 'Id:':
|
||||
ids.append(line.split()[1])
|
||||
return ids
|
||||
|
||||
|
||||
def main():
|
||||
subsystems = {}
|
||||
|
||||
command = create_socketfile()
|
||||
|
||||
for test in test_requests:
|
||||
# Remove any old ids
|
||||
del data['id']
|
||||
del data['id2']
|
||||
|
||||
# Run setup code to force MPD into known state
|
||||
for setup in setup_requests:
|
||||
command.write(setup % data + '\n')
|
||||
wait(command)
|
||||
|
||||
data['id'], data['id2'] = collect_ids(command)[:2]
|
||||
|
||||
# This connection needs to be make after setup commands are done or
|
||||
# else they will cause idle events.
|
||||
idle = create_socketfile()
|
||||
|
||||
# Wait for new idle events
|
||||
idle.write('idle\n')
|
||||
|
||||
test = test % data
|
||||
|
||||
logging.debug('idle: %s', repr('idle'))
|
||||
logging.debug('command: %s', repr(test))
|
||||
|
||||
command.write(test + '\n')
|
||||
wait(command, prefix='command')
|
||||
|
||||
while True:
|
||||
try:
|
||||
line = idle.readline().rstrip()
|
||||
except socket.timeout:
|
||||
# Abort try if we time out.
|
||||
idle.write('noidle\n')
|
||||
break
|
||||
|
||||
logging.debug('idle: %s', repr(line))
|
||||
|
||||
if line == 'OK':
|
||||
break
|
||||
|
||||
request_type = test.split()[0]
|
||||
subsystem = line.split()[1]
|
||||
subsystems.setdefault(request_type, set()).add(subsystem)
|
||||
|
||||
logging.debug('---')
|
||||
|
||||
pprint.pprint(subsystems)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Loading…
Reference in New Issue
Block a user