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:
Stein Magnus Jodal 2011-07-27 00:19:04 +02:00
commit 7a2984528c
40 changed files with 1952 additions and 1412 deletions

View File

@ -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

View File

@ -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')

View File

@ -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):

View File

@ -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',

View File

@ -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

View File

@ -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()

View File

@ -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

View File

@ -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

View File

@ -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)]

View File

@ -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()

View File

@ -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

View File

@ -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')

View File

@ -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(

View File

@ -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

View File

@ -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()

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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')

View 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])

View 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')

View File

@ -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')

View 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.

View 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')

View File

@ -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')

View 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')

View 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')

View File

@ -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')

View 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')

View File

@ -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')

View 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')

View 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')

View 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')

View File

@ -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)

View File

@ -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()

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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
View 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()