From d500e7a372937101f589ca1c210962adba2bf337 Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Sat, 31 Jul 2010 09:44:15 +0200 Subject: [PATCH 01/11] Fix docs typo --- mopidy/backends/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mopidy/backends/__init__.py b/mopidy/backends/__init__.py index d6cc4913..b9a4dd86 100644 --- a/mopidy/backends/__init__.py +++ b/mopidy/backends/__init__.py @@ -98,11 +98,11 @@ class BaseCurrentPlaylistController(object): @property def tracks(self): """ - List of :class:`mopidy.model.Track` in the current playlist. + List of :class:`mopidy.models.Track` in the current playlist. Read-only. """ - return [t[1] for t in self._cp_tracks] + return [ct[1] for ct in self._cp_tracks] def _get_cp_track(self, **criteria): matches = self._cp_tracks From 8c5d6aba7821d53a3dd119984e41dcf0c3f50a49 Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Sat, 31 Jul 2010 17:58:41 +0200 Subject: [PATCH 02/11] docs: Darker background on sidebar --- docs/_themes/nature/static/nature.css_t | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/_themes/nature/static/nature.css_t b/docs/_themes/nature/static/nature.css_t index 8762e019..63ef80d6 100644 --- a/docs/_themes/nature/static/nature.css_t +++ b/docs/_themes/nature/static/nature.css_t @@ -30,7 +30,7 @@ hr{ } div.document { - background-color: #fafafa; + background-color: #eeeeee; } div.body { From 7a216f2c11f385a60ee65ac1a052fca538ed8621 Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Sat, 31 Jul 2010 18:06:49 +0200 Subject: [PATCH 03/11] docs: Turn on the viewcode extension --- docs/conf.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index a513882a..0a92ba7c 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -26,7 +26,8 @@ import mopidy # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = ['sphinx.ext.autodoc', 'autodoc_private_members', - 'sphinx.ext.graphviz', 'sphinx.ext.inheritance_diagram'] + 'sphinx.ext.graphviz', 'sphinx.ext.inheritance_diagram', + 'sphinx.ext.viewcode'] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] From 25cf97cb69b7d9916ffcac0e98463412b1648678 Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Sat, 31 Jul 2010 18:11:35 +0200 Subject: [PATCH 04/11] docs: Turn on extlinks extension --- docs/conf.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index 0a92ba7c..98b1e2c1 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -27,7 +27,7 @@ import mopidy # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = ['sphinx.ext.autodoc', 'autodoc_private_members', 'sphinx.ext.graphviz', 'sphinx.ext.inheritance_diagram', - 'sphinx.ext.viewcode'] + 'sphinx.ext.extlinks', 'sphinx.ext.viewcode'] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] @@ -199,3 +199,5 @@ latex_documents = [ # If false, no module index is generated. #latex_use_modindex = True + +extlinks = {'issue': ('http://github.com/jodal/mopidy/issues#issue/%s', 'GH-')} From ac175ef51591eb65330710cad8a2ca53d74645de Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Sat, 31 Jul 2010 18:17:18 +0200 Subject: [PATCH 05/11] docs: Require Sphinx >= 1.0 --- docs/conf.py | 2 ++ requirements-docs.txt | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index 98b1e2c1..b190e8a9 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -200,4 +200,6 @@ latex_documents = [ #latex_use_modindex = True +needs_sphinx = '1.0' + extlinks = {'issue': ('http://github.com/jodal/mopidy/issues#issue/%s', 'GH-')} diff --git a/requirements-docs.txt b/requirements-docs.txt index 0d593422..c75793d9 100644 --- a/requirements-docs.txt +++ b/requirements-docs.txt @@ -1,2 +1,2 @@ -Sphinx +Sphinx >= 1.0 pygraphviz From 8575c3322717eda1b161a422abf93922e81f4d7c Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Sat, 31 Jul 2010 19:49:21 +0200 Subject: [PATCH 06/11] docs: Fix list indentation and add GitHub issue links --- docs/changes.rst | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/docs/changes.rst b/docs/changes.rst index 0c4cac13..2a0a578a 100644 --- a/docs/changes.rst +++ b/docs/changes.rst @@ -16,7 +16,7 @@ We got an updated :doc:`release roadmap `! - Support IPv6. - ``addid`` responds properly on errors instead of crashing. - ``commands`` support, which makes RelaXXPlayer work with Mopidy. (Fixes: - GH-6) + :issue:`6`) - Does no longer crash on invalid data, i.e. non-UTF-8 data. - ``ACK`` error messages are now MPD-compliant, which should make clients handle errors from Mopidy better. @@ -34,7 +34,7 @@ We got an updated :doc:`release roadmap `! - Libspotify backend: - Fix choppy playback using the Libspotify backend by using blocking ALSA - mode. (Fixes: GH-7) + mode. (Fixes: :issue:`7`) - Backend API: @@ -86,19 +86,19 @@ As always, report problems at our IRC channel or our issue tracker. Thanks! - Backend API changes: - - Removed ``backend.playback.volume`` wrapper. Use ``backend.mixer.volume`` - directly. - - Renamed ``backend.playback.playlist_position`` to - ``current_playlist_position`` to match naming of ``current_track``. - - Replaced ``get_by_id()`` with a more flexible ``get(**criteria)``. + - Removed ``backend.playback.volume`` wrapper. Use ``backend.mixer.volume`` + directly. + - Renamed ``backend.playback.playlist_position`` to + ``current_playlist_position`` to match naming of ``current_track``. + - Replaced ``get_by_id()`` with a more flexible ``get(**criteria)``. - Merged the ``gstreamer`` branch from Thomas Adamcik: - - More than 200 new tests, and thus several bugfixes to existing code. - - Several new generic features, like shuffle, consume, and playlist repeat. - (Fixes: GH-3) - - **[Work in Progress]** A new backend for playing music from a local music - archive using the Gstreamer library. + - More than 200 new tests, and thus several bugfixes to existing code. + - Several new generic features, like shuffle, consume, and playlist repeat. + (Fixes: :issue:`3`) + - **[Work in Progress]** A new backend for playing music from a local music + archive using the Gstreamer library. - Made :class:`mopidy.mixers.alsa.AlsaMixer` work on machines without a mixer named "Master". From 64544a0b71c324f83344d413fdf8b95df97ae83d Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Sat, 31 Jul 2010 19:51:03 +0200 Subject: [PATCH 07/11] Use cp_track in CurrentPlaylistCtl and PlaybackCtl A new data structure called ``cp_track`` is now used in the current playlist controller and the playback controller. A ``cp_track`` is a two-tuple of (CPID integer, mopidy.models.Track), identifying an instance of a track uniquely within the current playlist. This fixes issues with using playlists with multiple instances of the same track. --- docs/changes.rst | 23 ++++- mopidy/backends/__init__.py | 194 +++++++++++++++++++++++------------- mopidy/mpd/frontend.py | 43 ++++---- tests/backends/base.py | 47 ++++----- tests/mpd/frontend_test.py | 39 +++----- 5 files changed, 207 insertions(+), 139 deletions(-) diff --git a/docs/changes.rst b/docs/changes.rst index 2a0a578a..7b312dd8 100644 --- a/docs/changes.rst +++ b/docs/changes.rst @@ -38,16 +38,35 @@ We got an updated :doc:`release roadmap `! - Backend API: + - A new data structure called ``cp_track`` is now used in the current + playlist controller and the playback controller. A ``cp_track`` is a + two-tuple of (CPID integer, :class:`mopidy.models.Track`), identifying an + instance of a track uniquely within the current playlist. - :meth:`mopidy.backends.BaseCurrentPlaylistController.load()` now accepts lists of :class:`mopidy.models.Track` instead of :class:`mopidy.models.Playlist`, as none of the other fields on the ``Playlist`` model was in use. - :meth:`mopidy.backends.BaseCurrentPlaylistController.remove()` now takes criterias, just like - :meth:`mopidy.backends.BaseCurrentPlaylistController.get()`, and not the - track to remove. + :meth:`mopidy.backends.BaseCurrentPlaylistController.get()`. + - :meth:`mopidy.backends.BaseCurrentPlaylistController.get()` now returns a + ``cp_track``. - :attr:`mopidy.backends.BaseCurrentPlaylistController.tracks` is now read-only. Use the methods to change its contents. + - :attr:`mopidy.backends.BaseCurrentPlaylistController.cp_tracks` is a + read-only list of ``cp_track``. Use the methods to change its contents. + - :attr:`mopidy.backends.BasePlaybackController.current_track` is now + just for convenience and read-only. To set the current track, assign a + ``cp_track`` to + :attr:`mopidy.backends.BasePlaybackController.current_cp_track`. + - :attr:`mopidy.backends.BasePlaybackController.current_cpid` is the + read-only CPID of the current track. + - :attr:`mopidy.backends.BasePlaybackController.next_cp_track` is the + next ``cp_track`` in the playlist. + - :attr:`mopidy.backends.BasePlaybackController.previous_cp_track` is + the previous ``cp_track`` in the playlist. + - :meth:`mopidy.backends.BasePlaybackController.play()` now takes a + ``cp_track``. 0.1.0a2 (2010-06-02) diff --git a/mopidy/backends/__init__.py b/mopidy/backends/__init__.py index b9a4dd86..e659fa80 100644 --- a/mopidy/backends/__init__.py +++ b/mopidy/backends/__init__.py @@ -95,6 +95,15 @@ class BaseCurrentPlaylistController(object): """Cleanup after component.""" pass + @property + def cp_tracks(self): + """ + List of two-tuples of (CPID integer, :class:`mopidy.models.Track`). + + Read-only. + """ + return [copy(ct) for ct in self._cp_tracks] + @property def tracks(self): """ @@ -104,23 +113,6 @@ class BaseCurrentPlaylistController(object): """ return [ct[1] for ct in self._cp_tracks] - def _get_cp_track(self, **criteria): - matches = self._cp_tracks - for (key, value) in criteria.iteritems(): - if key == 'cpid': - matches = filter(lambda ct: ct[0] == value, matches) - else: - matches = filter(lambda ct: getattr(ct[1], key) == value, - matches) - if len(matches) == 1: - return matches[0] - criteria_string = ', '.join( - ['%s=%s' % (k, v) for (k, v) in criteria.iteritems()]) - if len(matches) == 0: - raise LookupError(u'"%s" match no tracks' % criteria_string) - else: - raise LookupError(u'"%s" match multiple tracks' % criteria_string) - def add(self, track, at_position=None): """ Add the track to the end of, or at the given position in the current @@ -142,7 +134,7 @@ class BaseCurrentPlaylistController(object): def clear(self): """Clear the current playlist.""" self.backend.playback.stop() - self.backend.playback.current_track = None + self.backend.playback.current_cp_track = None self._cp_tracks = [] self.version += 1 @@ -162,9 +154,23 @@ class BaseCurrentPlaylistController(object): :param criteria: on or more criteria to match by :type criteria: dict - :rtype: :class:`mopidy.models.Track` + :rtype: two-tuple (CPID integer, :class:`mopidy.models.Track`) """ - return self._get_cp_track(**criteria)[1] + matches = self._cp_tracks + for (key, value) in criteria.iteritems(): + if key == 'cpid': + matches = filter(lambda ct: ct[0] == value, matches) + else: + matches = filter(lambda ct: getattr(ct[1], key) == value, + matches) + if len(matches) == 1: + return matches[0] + criteria_string = ', '.join( + ['%s=%s' % (k, v) for (k, v) in criteria.iteritems()]) + if len(matches) == 0: + raise LookupError(u'"%s" match no tracks' % criteria_string) + else: + raise LookupError(u'"%s" match multiple tracks' % criteria_string) def load(self, tracks): """ @@ -214,13 +220,13 @@ class BaseCurrentPlaylistController(object): """ Remove the track from the current playlist. - Uses :meth:`get` to lookup the track to remove. + Uses :meth:`get()` to lookup the track to remove. :param criteria: on or more criteria to match by :type criteria: dict :type track: :class:`mopidy.models.Track` """ - cp_track = self._get_cp_track(**criteria) + cp_track = self.get(**criteria) position = self._cp_tracks.index(cp_track) del self._cp_tracks[position] self.version += 1 @@ -338,11 +344,11 @@ class BasePlaybackController(object): #: Tracks are not removed from the playlist. consume = False - #: The CPID (current playlist ID) of :attr:`current_track`. - current_cpid = 0 # TODO Get the correct CPID - - #: The currently playing or selected :class:`mopidy.models.Track`. - current_track = None + #: The currently playing or selected track + #: + #: A two-tuple of (CPID integer, :class:`mopidy.models.Track`) or + #: :class:`None`. + current_cp_track = None #: :class:`True` #: Tracks are selected at random from the playlist. @@ -375,69 +381,122 @@ class BasePlaybackController(object): """Cleanup after component.""" pass + @property + def current_cpid(self): + """ + The CPID (current playlist ID) of :attr:`current_track`. + + Read-only. Extracted from :attr:`current_cp_track` for convenience. + """ + if self.current_cp_track is None: + return None + return self.current_cp_track[0] + + @property + def current_track(self): + """ + The currently playing or selected :class:`mopidy.models.Track`. + + Read-only. Extracted from :attr:`current_cp_track` for convenience. + """ + if self.current_cp_track is None: + return None + return self.current_cp_track[1] + @property def current_playlist_position(self): """The position of the current track in the current playlist.""" - if self.current_track is None: + if self.current_cp_track is None: return None try: - return self.backend.current_playlist.tracks.index( - self.current_track) + return self.backend.current_playlist.cp_tracks.index( + self.current_cp_track) except ValueError: return None @property def next_track(self): """ - The next :class:`mopidy.models.Track` in the playlist. + The next track in the playlist. + + A :class:`mopidy.models.Track` extracted from :attr:`next_cp_track` for + convenience. + """ + next_cp_track = self.next_cp_track + if next_cp_track is None: + return None + return next_cp_track[1] + + @property + def next_cp_track(self): + """ + The next track in the playlist. + + A two-tuple of (CPID integer, :class:`mopidy.models.Track`). For normal playback this is the next track in the playlist. If repeat is enabled the next track can loop around the playlist. When random is enabled this should be a random track, all tracks should be played once before the list repeats. """ - tracks = self.backend.current_playlist.tracks + cp_tracks = self.backend.current_playlist.cp_tracks - if not tracks: + if not cp_tracks: return None if self.random and not self._shuffled: if self.repeat or self._first_shuffle: logger.debug('Shuffling tracks') - self._shuffled = tracks + self._shuffled = cp_tracks random.shuffle(self._shuffled) self._first_shuffle = False if self._shuffled: return self._shuffled[0] - if self.current_track is None: - return tracks[0] + if self.current_cp_track is None: + return cp_tracks[0] if self.repeat: - return tracks[(self.current_playlist_position + 1) % len(tracks)] + return cp_tracks[ + (self.current_playlist_position + 1) % len(cp_tracks)] try: - return tracks[self.current_playlist_position + 1] + return cp_tracks[self.current_playlist_position + 1] except IndexError: return None @property def previous_track(self): """ - The previous :class:`mopidy.models.Track` in the playlist. + The previous track in the playlist. + + A :class:`mopidy.models.Track` extracted from :attr:`previous_cp_track` + for convenience. + """ + previous_cp_track = self.previous_cp_track + if previous_cp_track is None: + return None + return previous_cp_track[1] + + @property + def previous_cp_track(self): + """ + The previous track in the playlist. + + A two-tuple of (CPID integer, :class:`mopidy.models.Track`). For normal playback this is the previous track in the playlist. If random and/or consume is enabled it should return the current track instead. """ if self.repeat or self.consume or self.random: - return self.current_track + return self.current_cp_track - if self.current_track is None or self.current_playlist_position == 0: + if self.current_cp_track is None or self.current_playlist_position == 0: return None - return self.backend.current_playlist.tracks[ + return self.backend.current_playlist.cp_tracks[ self.current_playlist_position - 1] @property @@ -507,11 +566,11 @@ class BasePlaybackController(object): Typically called by :class:`mopidy.process.CoreProcess` after a message from a library thread is received. """ - if self.next_track is not None: + if self.next_cp_track is not None: self.next() else: self.stop() - self.current_track = None + self.current_cp_track = None def new_playlist_loaded_callback(self): """ @@ -520,7 +579,7 @@ class BasePlaybackController(object): Typically called by :class:`mopidy.process.CoreProcess` after a message from a library thread is received. """ - self.current_track = None + self.current_cp_track = None self._first_shuffle = True self._shuffled = [] @@ -534,23 +593,23 @@ class BasePlaybackController(object): def next(self): """Play the next track.""" - original_track = self.current_track + original_cp_track = self.current_cp_track if self.state == self.STOPPED: return - elif self.next_track is not None and self._next(self.next_track): - self.current_track = self.next_track + elif self.next_cp_track is not None and self._next(self.next_track): + self.current_cp_track = self.next_cp_track self.state = self.PLAYING - elif self.next_track is None: + elif self.next_cp_track is None: self.stop() - self.current_track = None + self.current_cp_track = None # FIXME handle in play aswell? if self.consume: - self.backend.current_playlist.remove(id=original_track.id) + self.backend.current_playlist.remove(cpid=original_cp_track[0]) - if self.random and self.current_track in self._shuffled: - self._shuffled.remove(self.current_track) + if self.random and self.current_cp_track in self._shuffled: + self._shuffled.remove(self.current_cp_track) def _next(self, track): return self._play(track) @@ -563,41 +622,42 @@ class BasePlaybackController(object): def _pause(self): raise NotImplementedError - def play(self, track=None): + def play(self, cp_track=None): """ Play the given track or the currently active track. - :param track: track to play - :type track: :class:`mopidy.models.Track` or :class:`None` + :param cp_track: track to play + :type cp_track: two-tuple (CPID integer, :class:`mopidy.models.Track`) + or :class:`None` """ - if track: - assert track in self.backend.current_playlist.tracks - elif not self.current_track: - track = self.next_track + if cp_track is not None: + assert cp_track in self.backend.current_playlist.cp_tracks + elif not self.current_cp_track: + cp_track = self.next_cp_track - if self.state == self.PAUSED and track is None: + if self.state == self.PAUSED and cp_track is None: self.resume() - elif track is not None and self._play(track): - self.current_track = track + elif cp_track is not None and self._play(cp_track[1]): + self.current_cp_track = cp_track self.state = self.PLAYING # TODO Do something sensible when _play() returns False, like calling # next(). Adding this todo instead of just implementing it as I want a # test case first. - if self.random and self.current_track in self._shuffled: - self._shuffled.remove(self.current_track) + if self.random and self.current_cp_track in self._shuffled: + self._shuffled.remove(self.current_cp_track) def _play(self, track): raise NotImplementedError def previous(self): """Play the previous track.""" - if (self.previous_track is not None + if (self.previous_cp_track is not None and self.state != self.STOPPED and self._previous(self.previous_track)): - self.current_track = self.previous_track + self.current_cp_track = self.previous_cp_track self.state = self.PLAYING def _previous(self, track): diff --git a/mopidy/mpd/frontend.py b/mopidy/mpd/frontend.py index a019f454..7aa5d6e0 100644 --- a/mopidy/mpd/frontend.py +++ b/mopidy/mpd/frontend.py @@ -369,8 +369,8 @@ class MpdFrontend(object): """ cpid = int(cpid) to = int(to) - track = self.backend.current_playlist.get(cpid=cpid) - position = self.backend.current_playlist.tracks.index(track) + cp_track = self.backend.current_playlist.get(cpid=cpid) + position = self.backend.current_playlist.cp_tracks.index(cp_track) self.backend.current_playlist.move(position, position + 1, to) @handle_pattern(r'^playlist$') @@ -404,8 +404,8 @@ class MpdFrontend(object): """ if tag == 'filename': try: - track = self.backend.current_playlist.get(uri=needle) - return track.mpd_format() + cp_track = self.backend.current_playlist.get(uri=needle) + return cp_track[1].mpd_format() except LookupError: return None raise MpdNotImplemented # TODO @@ -423,9 +423,10 @@ class MpdFrontend(object): if cpid is not None: try: cpid = int(cpid) - track = self.backend.current_playlist.get(cpid=cpid) - position = self.backend.current_playlist.tracks.index(track) - return track.mpd_format(position=position, cpid=cpid) + cp_track = self.backend.current_playlist.get(cpid=cpid) + position = self.backend.current_playlist.cp_tracks.index( + cp_track) + return cp_track[1].mpd_format(position=position, cpid=cpid) except LookupError: raise MpdNoExistError(u'No such song', command=u'playlistid') else: @@ -571,10 +572,10 @@ class MpdFrontend(object): """ cpid1 = int(cpid1) cpid2 = int(cpid2) - track1 = self.backend.current_playlist.get(cpid=cpid1) - track2 = self.backend.current_playlist.get(cpid=cpid2) - position1 = self.backend.current_playlist.tracks.index(track1) - position2 = self.backend.current_playlist.tracks.index(track2) + cp_track1 = self.backend.current_playlist.get(cpid=cpid1) + cp_track2 = self.backend.current_playlist.get(cpid=cpid2) + position1 = self.backend.current_playlist.cp_tracks.index(cp_track1) + position2 = self.backend.current_playlist.cp_tracks.index(cp_track2) self._current_playlist_swap(position1, position2) @handle_pattern(r'^$') @@ -914,10 +915,10 @@ class MpdFrontend(object): cpid = int(cpid) try: if cpid == -1: - track = self.backend.current_playlist.tracks[0] + cp_track = self.backend.current_playlist.cp_tracks[0] else: - track = self.backend.current_playlist.get(cpid=cpid) - return self.backend.playback.play(track) + cp_track = self.backend.current_playlist.get(cpid=cpid) + return self.backend.playback.play(cp_track) except LookupError: raise MpdNoExistError(u'No such song', command=u'playid') @@ -933,15 +934,16 @@ class MpdFrontend(object): *MPoD:* - - issues ``play "-1"`` after playlist replacement. + - issues ``play "-1"`` after playlist replacement to start playback at + the first track. """ songpos = int(songpos) try: if songpos == -1: - track = self.backend.current_playlist.tracks[0] + cp_track = self.backend.current_playlist.cp_tracks[0] else: - track = self.backend.current_playlist.tracks[songpos] - return self.backend.playback.play(track) + cp_track = self.backend.current_playlist.cp_tracks[songpos] + return self.backend.playback.play(cp_track) except IndexError: raise MpdArgError(u'Bad song index', command=u'play') @@ -1365,9 +1367,8 @@ class MpdFrontend(object): return int(self.backend.playback.single) def __status_status_songid(self): - # TODO Replace track.id with CPID - if self.backend.playback.current_track.id is not None: - return self.backend.playback.current_track.id + if self.backend.playback.current_cpid is not None: + return self.backend.playback.current_cpid else: return self.__status_status_songpos() diff --git a/tests/backends/base.py b/tests/backends/base.py index aa0f56e5..4c1ed0ee 100644 --- a/tests/backends/base.py +++ b/tests/backends/base.py @@ -63,14 +63,13 @@ class BaseCurrentPlaylistControllerTest(object): @populate_playlist def test_get_by_cpid(self): - track = self.controller.tracks[1] - cpid = self.controller._cp_tracks[1][0] # XXX Messing in internals - self.assertEqual(track, self.controller.get(cpid=cpid)) + cp_track = self.controller.cp_tracks[1] + self.assertEqual(cp_track, self.controller.get(cpid=cp_track[0])) @populate_playlist def test_get_by_id(self): - track = self.controller.tracks[1] - self.assertEqual(track, self.controller.get(id=track.id)) + cp_track = self.controller.cp_tracks[1] + self.assertEqual(cp_track, self.controller.get(id=cp_track[1].id)) @populate_playlist def test_get_by_id_raises_error_for_invalid_id(self): @@ -78,8 +77,8 @@ class BaseCurrentPlaylistControllerTest(object): @populate_playlist def test_get_by_uri(self): - track = self.controller.tracks[1] - self.assertEqual(track, self.controller.get(uri=track.uri)) + cp_track = self.controller.cp_tracks[1] + self.assertEqual(cp_track, self.controller.get(uri=cp_track[1].uri)) @populate_playlist def test_get_by_uri_raises_error_for_invalid_id(self): @@ -111,7 +110,7 @@ class BaseCurrentPlaylistControllerTest(object): def test_get_by_id_returns_unique_match(self): track = Track(id=1) self.controller.load([Track(id=13), track, Track(id=17)]) - self.assertEqual(track, self.controller.get(id=1)) + self.assertEqual(track, self.controller.get(id=1)[1]) def test_get_by_id_raises_error_if_multiple_matches(self): track = Track(id=1) @@ -133,7 +132,7 @@ class BaseCurrentPlaylistControllerTest(object): def test_get_by_uri_returns_unique_match(self): track = Track(uri='a') self.controller.load([Track(uri='z'), track, Track(uri='y')]) - self.assertEqual(track, self.controller.get(uri='a')) + self.assertEqual(track, self.controller.get(uri='a')[1]) def test_get_by_uri_raises_error_if_multiple_matches(self): track = Track(uri='a') @@ -158,16 +157,16 @@ class BaseCurrentPlaylistControllerTest(object): track2 = Track(id=1, uri='b') track3 = Track(id=2, uri='b') self.controller.load([track1, track2, track3]) - self.assertEqual(track1, self.controller.get(id=1, uri='a')) - self.assertEqual(track2, self.controller.get(id=1, uri='b')) - self.assertEqual(track3, self.controller.get(id=2, uri='b')) + self.assertEqual(track1, self.controller.get(id=1, uri='a')[1]) + self.assertEqual(track2, self.controller.get(id=1, uri='b')[1]) + self.assertEqual(track3, self.controller.get(id=2, uri='b')[1]) def test_get_by_criteria_that_is_not_present_in_all_elements(self): track1 = Track(id=1) track2 = Track(uri='b') track3 = Track(id=2) self.controller.load([track1, track2, track3]) - self.assertEqual(track1, self.controller.get(id=1)) + self.assertEqual(track1, self.controller.get(id=1)[1]) @populate_playlist def test_load_replaces_playlist(self): @@ -316,6 +315,7 @@ class BasePlaybackControllerTest(object): def setUp(self): self.backend = self.backend_class(mixer=DummyMixer()) self.playback = self.backend.playback + self.current_playlist = self.backend.current_playlist assert len(self.tracks) >= 3, \ 'Need at least three tracks to run tests.' @@ -349,12 +349,13 @@ class BasePlaybackControllerTest(object): @populate_playlist def test_play_track_state(self): self.assertEqual(self.playback.state, self.playback.STOPPED) - self.playback.play(self.tracks[-1]) + self.playback.play(self.current_playlist.cp_tracks[-1]) self.assertEqual(self.playback.state, self.playback.PLAYING) @populate_playlist def test_play_track_return_value(self): - self.assertEqual(self.playback.play(self.tracks[-1]), None) + self.assertEqual(self.playback.play( + self.current_playlist.cp_tracks[-1]), None) @populate_playlist def test_play_when_playing(self): @@ -379,17 +380,17 @@ class BasePlaybackControllerTest(object): @populate_playlist def test_play_track_sets_current_track(self): - self.playback.play(self.tracks[-1]) + self.playback.play(self.current_playlist.cp_tracks[-1]) self.assertEqual(self.playback.current_track, self.tracks[-1]) @populate_playlist def test_current_track_after_completed_playlist(self): - self.playback.play(self.tracks[-1]) + self.playback.play(self.current_playlist.cp_tracks[-1]) self.playback.end_of_track_callback() self.assertEqual(self.playback.state, self.playback.STOPPED) self.assertEqual(self.playback.current_track, None) - self.playback.play(self.tracks[-1]) + self.playback.play(self.current_playlist.cp_tracks[-1]) self.playback.next() self.assertEqual(self.playback.state, self.playback.STOPPED) self.assertEqual(self.playback.current_track, None) @@ -510,7 +511,7 @@ class BasePlaybackControllerTest(object): @populate_playlist def test_next_track_at_end_of_playlist(self): self.playback.play() - for track in self.tracks[1:]: + for track in self.current_playlist.cp_tracks[1:]: self.playback.next() self.assertEqual(self.playback.next_track, None) @@ -602,7 +603,7 @@ class BasePlaybackControllerTest(object): @populate_playlist def test_current_playlist_position_at_end_of_playlist(self): - self.playback.play(self.tracks[-1]) + self.playback.play(self.current_playlist.cp_tracks[-1]) self.playback.end_of_track_callback() self.assertEqual(self.playback.current_playlist_position, None) @@ -763,8 +764,8 @@ class BasePlaybackControllerTest(object): @populate_playlist def test_seek_beyond_end_of_song_for_last_track(self): - self.playback.play(self.tracks[-1]) - self.playback.seek(self.tracks[-1].length*100) + self.playback.play(self.current_playlist.cp_tracks[-1]) + self.playback.seek(self.current_playlist.tracks[-1].length * 100) self.assertEqual(self.playback.state, self.playback.STOPPED) @populate_playlist @@ -884,7 +885,7 @@ class BasePlaybackControllerTest(object): @populate_playlist def test_end_of_playlist_stops(self): - self.playback.play(self.tracks[-1]) + self.playback.play(self.current_playlist.cp_tracks[-1]) self.playback.end_of_track_callback() self.assertEqual(self.playback.state, self.playback.STOPPED) diff --git a/tests/mpd/frontend_test.py b/tests/mpd/frontend_test.py index ea26b526..1e92d715 100644 --- a/tests/mpd/frontend_test.py +++ b/tests/mpd/frontend_test.py @@ -119,7 +119,7 @@ class StatusHandlerTest(unittest.TestCase): def test_currentsong(self): track = Track() self.b.current_playlist.load([track]) - self.b.playback.current_track = track + self.b.playback.play() result = self.h.handle_request(u'currentsong') self.assert_(u'file: ' in result) self.assert_(u'Time: 0' in result) @@ -129,7 +129,7 @@ class StatusHandlerTest(unittest.TestCase): 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'Id: 1' in result) self.assert_(u'OK' in result) def test_currentsong_without_song(self): @@ -257,32 +257,22 @@ class StatusHandlerTest(unittest.TestCase): self.assertEqual(result['state'], 'pause') def test_status_method_when_playlist_loaded_contains_song(self): - track = Track() - self.b.current_playlist.load([track]) - self.b.playback.current_track = track + self.b.current_playlist.load([Track()]) + self.b.playback.play() result = dict(self.h._status_status()) self.assert_('song' in result) self.assert_(int(result['song']) >= 0) - def test_status_method_when_playlist_loaded_contains_pos_as_songid(self): - track = Track() - self.b.current_playlist.load([track]) - self.b.playback.current_track = track - result = dict(self.h._status_status()) - self.assert_('songid' in result) - self.assert_(int(result['songid']) >= 0) - - def test_status_method_when_playlist_loaded_contains_id_as_songid(self): - track = Track(id=1) - self.b.current_playlist.load([track]) - self.b.playback.current_track = track + def test_status_method_when_playlist_loaded_contains_cpid_as_songid(self): + self.b.current_playlist.load([Track()]) + self.b.playback.play() result = dict(self.h._status_status()) self.assert_('songid' in result) self.assertEqual(int(result['songid']), 1) def test_status_method_when_playing_contains_time_with_no_length(self): - self.b.playback.current_track = Track(length=None) - self.b.playback.state = self.b.playback.PLAYING + self.b.current_playlist.load([Track(length=None)]) + self.b.playback.play() result = dict(self.h._status_status()) self.assert_('time' in result) (position, total) = result['time'].split(':') @@ -291,8 +281,8 @@ class StatusHandlerTest(unittest.TestCase): self.assert_(position <= total) def test_status_method_when_playing_contains_time_with_length(self): - self.b.playback.current_track = Track(length=10000) - self.b.playback.state = self.b.playback.PLAYING + self.b.current_playlist.load([Track(length=10000)]) + self.b.playback.play() result = dict(self.h._status_status()) self.assert_('time' in result) (position, total) = result['time'].split(':') @@ -308,8 +298,8 @@ class StatusHandlerTest(unittest.TestCase): self.assertEqual(int(result['elapsed']), 59123) def test_status_method_when_playing_contains_bitrate(self): - self.b.playback.state = self.b.playback.PLAYING - self.b.playback.current_track = Track(bitrate=320) + self.b.current_playlist.load([Track(bitrate=320)]) + self.b.playback.play() result = dict(self.h._status_status()) self.assert_('bitrate' in result) self.assertEqual(int(result['bitrate']), 320) @@ -448,7 +438,6 @@ class PlaybackControlHandlerTest(unittest.TestCase): def test_pause_off(self): track = Track() self.b.current_playlist.load([track]) - self.b.playback.current_track = track self.h.handle_request(u'play "0"') self.h.handle_request(u'pause "1"') result = self.h.handle_request(u'pause "0"') @@ -458,7 +447,6 @@ class PlaybackControlHandlerTest(unittest.TestCase): def test_pause_on(self): track = Track() self.b.current_playlist.load([track]) - self.b.playback.current_track = track self.h.handle_request(u'play "0"') result = self.h.handle_request(u'pause "1"') self.assert_(u'OK' in result) @@ -467,7 +455,6 @@ class PlaybackControlHandlerTest(unittest.TestCase): def test_play_without_pos(self): track = Track() self.b.current_playlist.load([track]) - self.b.playback.current_track = track self.b.playback.state = self.b.playback.PAUSED result = self.h.handle_request(u'play') self.assert_(u'OK' in result) From 04304dafd83b0c018d9812ea7b9405560c7702b7 Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Sat, 31 Jul 2010 20:12:51 +0200 Subject: [PATCH 08/11] tests: Split mpd/frontend_test into 11 files --- tests/mpd/audio_output_test.py | 26 + tests/mpd/command_list_test.py | 58 ++ tests/mpd/connection_test.py | 31 + tests/mpd/current_playlist_test.py | 393 +++++++++ tests/mpd/frontend_test.py | 1274 ---------------------------- tests/mpd/music_db_test.py | 168 ++++ tests/mpd/playback_test.py | 217 +++++ tests/mpd/reflection_test.py | 44 + tests/mpd/request_handler_test.py | 48 ++ tests/mpd/status_test.py | 204 +++++ tests/mpd/stickers_test.py | 41 + tests/mpd/stored_playlists_test.py | 87 ++ 12 files changed, 1317 insertions(+), 1274 deletions(-) create mode 100644 tests/mpd/audio_output_test.py create mode 100644 tests/mpd/command_list_test.py create mode 100644 tests/mpd/connection_test.py create mode 100644 tests/mpd/current_playlist_test.py delete mode 100644 tests/mpd/frontend_test.py create mode 100644 tests/mpd/music_db_test.py create mode 100644 tests/mpd/playback_test.py create mode 100644 tests/mpd/reflection_test.py create mode 100644 tests/mpd/request_handler_test.py create mode 100644 tests/mpd/status_test.py create mode 100644 tests/mpd/stickers_test.py create mode 100644 tests/mpd/stored_playlists_test.py diff --git a/tests/mpd/audio_output_test.py b/tests/mpd/audio_output_test.py new file mode 100644 index 00000000..6e6ea5b5 --- /dev/null +++ b/tests/mpd/audio_output_test.py @@ -0,0 +1,26 @@ +import unittest + +from mopidy.backends.dummy import DummyBackend +from mopidy.mixers.dummy import DummyMixer +from mopidy.mpd import frontend + +class AudioOutputHandlerTest(unittest.TestCase): + def setUp(self): + self.m = DummyMixer() + self.b = DummyBackend(mixer=self.m) + self.h = frontend.MpdFrontend(backend=self.b) + + def test_enableoutput(self): + result = self.h.handle_request(u'enableoutput "0"') + self.assert_(u'ACK [0@0] {} Not implemented' in result) + + def test_disableoutput(self): + result = self.h.handle_request(u'disableoutput "0"') + self.assert_(u'ACK [0@0] {} Not implemented' in result) + + def test_outputs(self): + result = self.h.handle_request(u'outputs') + self.assert_(u'outputid: 0' in result) + self.assert_(u'outputname: DummyBackend' in result) + self.assert_(u'outputenabled: 1' in result) + self.assert_(u'OK' in result) diff --git a/tests/mpd/command_list_test.py b/tests/mpd/command_list_test.py new file mode 100644 index 00000000..69eb19ac --- /dev/null +++ b/tests/mpd/command_list_test.py @@ -0,0 +1,58 @@ +import unittest + +from mopidy.backends.dummy import DummyBackend +from mopidy.mixers.dummy import DummyMixer +from mopidy.mpd import frontend + +class CommandListsTest(unittest.TestCase): + def setUp(self): + self.m = DummyMixer() + self.b = DummyBackend(mixer=self.m) + self.h = frontend.MpdFrontend(backend=self.b) + + def test_command_list_begin(self): + result = self.h.handle_request(u'command_list_begin') + self.assert_(result is None) + + def test_command_list_end(self): + self.h.handle_request(u'command_list_begin') + result = self.h.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.h.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.h.handle_request(u'command_list_begin') + self.assertEqual([], self.h.command_list) + self.assertEqual(False, self.h.command_list_ok) + self.h.handle_request(u'ping') + self.assert_(u'ping' in self.h.command_list) + result = self.h.handle_request(u'command_list_end') + self.assert_(u'OK' in result) + self.assertEqual(False, self.h.command_list) + + def test_command_list_with_error_returns_ack_with_correct_index(self): + self.h.handle_request(u'command_list_begin') + self.h.handle_request(u'play') # Known command + self.h.handle_request(u'paly') # Unknown command + result = self.h.handle_request(u'command_list_end') + self.assertEqual(result[0], u'ACK [5@1] {} unknown command "paly"') + + def test_command_list_ok_begin(self): + result = self.h.handle_request(u'command_list_ok_begin') + self.assert_(result is None) + + def test_command_list_ok_with_ping(self): + self.h.handle_request(u'command_list_ok_begin') + self.assertEqual([], self.h.command_list) + self.assertEqual(True, self.h.command_list_ok) + self.h.handle_request(u'ping') + self.assert_(u'ping' in self.h.command_list) + result = self.h.handle_request(u'command_list_end') + self.assert_(u'list_OK' in result) + self.assert_(u'OK' in result) + self.assertEqual(False, self.h.command_list) + self.assertEqual(False, self.h.command_list_ok) diff --git a/tests/mpd/connection_test.py b/tests/mpd/connection_test.py new file mode 100644 index 00000000..82c7d5c8 --- /dev/null +++ b/tests/mpd/connection_test.py @@ -0,0 +1,31 @@ +import unittest + +from mopidy.backends.dummy import DummyBackend +from mopidy.mixers.dummy import DummyMixer +from mopidy.mpd import frontend + +class ConnectionHandlerTest(unittest.TestCase): + def setUp(self): + self.m = DummyMixer() + self.b = DummyBackend(mixer=self.m) + self.h = frontend.MpdFrontend(backend=self.b) + + def test_close(self): + result = self.h.handle_request(u'close') + self.assert_(u'OK' in result) + + def test_empty_request(self): + result = self.h.handle_request(u'') + self.assert_(u'OK' in result) + + def test_kill(self): + result = self.h.handle_request(u'kill') + self.assert_(u'OK' in result) + + def test_password(self): + result = self.h.handle_request(u'password "secret"') + self.assert_(u'ACK [0@0] {} Not implemented' in result) + + def test_ping(self): + result = self.h.handle_request(u'ping') + self.assert_(u'OK' in result) diff --git a/tests/mpd/current_playlist_test.py b/tests/mpd/current_playlist_test.py new file mode 100644 index 00000000..09e61b63 --- /dev/null +++ b/tests/mpd/current_playlist_test.py @@ -0,0 +1,393 @@ +import unittest + +from mopidy.backends.dummy import DummyBackend +from mopidy.mixers.dummy import DummyMixer +from mopidy.models import Track +from mopidy.mpd import frontend + +class CurrentPlaylistHandlerTest(unittest.TestCase): + def setUp(self): + self.m = DummyMixer() + self.b = DummyBackend(mixer=self.m) + self.h = frontend.MpdFrontend(backend=self.b) + + def test_add(self): + needle = Track(uri='dummy://foo') + self.b.library._library = [Track(), Track(), needle, Track()] + self.b.current_playlist.load( + [Track(), Track(), Track(), Track(), Track()]) + self.assertEqual(len(self.b.current_playlist.tracks), 5) + result = self.h.handle_request(u'add "dummy://foo"') + self.assertEqual(len(self.b.current_playlist.tracks), 6) + self.assertEqual(self.b.current_playlist.tracks[5], needle) + self.assertEqual(len(result), 1) + self.assert_(u'OK' in result) + + def test_add_with_uri_not_found_in_library_should_ack(self): + result = self.h.handle_request(u'add "dummy://foo"') + self.assertEqual(result[0], + u'ACK [50@0] {add} directory or file not found') + + def test_addid_without_songpos(self): + needle = Track(uri='dummy://foo', id=137) + self.b.library._library = [Track(), Track(), needle, Track()] + self.b.current_playlist.load( + [Track(), Track(), Track(), Track(), Track()]) + self.assertEqual(len(self.b.current_playlist.tracks), 5) + result = self.h.handle_request(u'addid "dummy://foo"') + self.assertEqual(len(self.b.current_playlist.tracks), 6) + self.assertEqual(self.b.current_playlist.tracks[5], needle) + self.assert_(u'Id: 137' in result) + self.assert_(u'OK' in result) + + def test_addid_with_songpos(self): + needle = Track(uri='dummy://foo', id=137) + self.b.library._library = [Track(), Track(), needle, Track()] + self.b.current_playlist.load( + [Track(), Track(), Track(), Track(), Track()]) + self.assertEqual(len(self.b.current_playlist.tracks), 5) + result = self.h.handle_request(u'addid "dummy://foo" "3"') + self.assertEqual(len(self.b.current_playlist.tracks), 6) + self.assertEqual(self.b.current_playlist.tracks[3], needle) + self.assert_(u'Id: 137' in result) + self.assert_(u'OK' in result) + + def test_addid_with_songpos_out_of_bounds_should_ack(self): + needle = Track(uri='dummy://foo', id=137) + self.b.library._library = [Track(), Track(), needle, Track()] + self.b.current_playlist.load( + [Track(), Track(), Track(), Track(), Track()]) + self.assertEqual(len(self.b.current_playlist.tracks), 5) + result = self.h.handle_request(u'addid "dummy://foo" "6"') + self.assertEqual(result[0], u'ACK [2@0] {addid} Bad song index') + + def test_addid_with_uri_not_found_in_library_should_ack(self): + result = self.h.handle_request(u'addid "dummy://foo"') + self.assertEqual(result[0], u'ACK [50@0] {addid} No such song') + + def test_clear(self): + self.b.current_playlist.load( + [Track(), Track(), Track(), Track(), Track()]) + self.assertEqual(len(self.b.current_playlist.tracks), 5) + result = self.h.handle_request(u'clear') + self.assertEqual(len(self.b.current_playlist.tracks), 0) + self.assertEqual(self.b.playback.current_track, None) + self.assert_(u'OK' in result) + + def test_delete_songpos(self): + self.b.current_playlist.load( + [Track(id=1), Track(id=2), Track(id=3), Track(id=4), Track(id=5)]) + self.assertEqual(len(self.b.current_playlist.tracks), 5) + result = self.h.handle_request(u'delete "2"') + self.assertEqual(len(self.b.current_playlist.tracks), 4) + self.assert_(u'OK' in result) + + def test_delete_songpos_out_of_bounds(self): + self.b.current_playlist.load( + [Track(), Track(), Track(), Track(), Track()]) + self.assertEqual(len(self.b.current_playlist.tracks), 5) + result = self.h.handle_request(u'delete "5"') + self.assertEqual(len(self.b.current_playlist.tracks), 5) + self.assertEqual(result[0], u'ACK [2@0] {delete} Bad song index') + + def test_delete_open_range(self): + self.b.current_playlist.load( + [Track(id=1), Track(id=2), Track(id=3), Track(id=4), Track(id=5)]) + self.assertEqual(len(self.b.current_playlist.tracks), 5) + result = self.h.handle_request(u'delete "1:"') + self.assertEqual(len(self.b.current_playlist.tracks), 1) + self.assert_(u'OK' in result) + + def test_delete_closed_range(self): + self.b.current_playlist.load( + [Track(id=1), Track(id=2), Track(id=3), Track(id=4), Track(id=5)]) + self.assertEqual(len(self.b.current_playlist.tracks), 5) + result = self.h.handle_request(u'delete "1:3"') + self.assertEqual(len(self.b.current_playlist.tracks), 3) + self.assert_(u'OK' in result) + + def test_delete_range_out_of_bounds(self): + self.b.current_playlist.load( + [Track(), Track(), Track(), Track(), Track()]) + self.assertEqual(len(self.b.current_playlist.tracks), 5) + result = self.h.handle_request(u'delete "5:7"') + self.assertEqual(len(self.b.current_playlist.tracks), 5) + self.assertEqual(result[0], u'ACK [2@0] {delete} Bad song index') + + def test_deleteid(self): + self.b.current_playlist.load([Track(), Track()]) + self.assertEqual(len(self.b.current_playlist.tracks), 2) + result = self.h.handle_request(u'deleteid "2"') + self.assertEqual(len(self.b.current_playlist.tracks), 1) + self.assert_(u'OK' in result) + + def test_deleteid_does_not_exist(self): + self.b.current_playlist.load([Track(), Track()]) + self.assertEqual(len(self.b.current_playlist.tracks), 2) + result = self.h.handle_request(u'deleteid "12345"') + self.assertEqual(len(self.b.current_playlist.tracks), 2) + self.assertEqual(result[0], u'ACK [50@0] {deleteid} No such song') + + def test_move_songpos(self): + self.b.current_playlist.load([ + Track(name='a'), Track(name='b'), Track(name='c'), + Track(name='d'), Track(name='e'), Track(name='f'), + ]) + result = self.h.handle_request(u'move "1" "0"') + self.assertEqual(self.b.current_playlist.tracks[0].name, 'b') + self.assertEqual(self.b.current_playlist.tracks[1].name, 'a') + self.assertEqual(self.b.current_playlist.tracks[2].name, 'c') + self.assertEqual(self.b.current_playlist.tracks[3].name, 'd') + self.assertEqual(self.b.current_playlist.tracks[4].name, 'e') + self.assertEqual(self.b.current_playlist.tracks[5].name, 'f') + self.assert_(u'OK' in result) + + def test_move_open_range(self): + self.b.current_playlist.load([ + Track(name='a'), Track(name='b'), Track(name='c'), + Track(name='d'), Track(name='e'), Track(name='f'), + ]) + result = self.h.handle_request(u'move "2:" "0"') + self.assertEqual(self.b.current_playlist.tracks[0].name, 'c') + self.assertEqual(self.b.current_playlist.tracks[1].name, 'd') + self.assertEqual(self.b.current_playlist.tracks[2].name, 'e') + self.assertEqual(self.b.current_playlist.tracks[3].name, 'f') + self.assertEqual(self.b.current_playlist.tracks[4].name, 'a') + self.assertEqual(self.b.current_playlist.tracks[5].name, 'b') + self.assert_(u'OK' in result) + + def test_move_closed_range(self): + self.b.current_playlist.load([ + Track(name='a'), Track(name='b'), Track(name='c'), + Track(name='d'), Track(name='e'), Track(name='f'), + ]) + result = self.h.handle_request(u'move "1:3" "0"') + self.assertEqual(self.b.current_playlist.tracks[0].name, 'b') + self.assertEqual(self.b.current_playlist.tracks[1].name, 'c') + self.assertEqual(self.b.current_playlist.tracks[2].name, 'a') + self.assertEqual(self.b.current_playlist.tracks[3].name, 'd') + self.assertEqual(self.b.current_playlist.tracks[4].name, 'e') + self.assertEqual(self.b.current_playlist.tracks[5].name, 'f') + self.assert_(u'OK' in result) + + def test_moveid(self): + self.b.current_playlist.load([ + Track(name='a'), Track(name='b'), Track(name='c'), + Track(name='d'), Track(name='e'), Track(name='f'), + ]) + result = self.h.handle_request(u'moveid "5" "2"') + self.assertEqual(self.b.current_playlist.tracks[0].name, 'a') + self.assertEqual(self.b.current_playlist.tracks[1].name, 'b') + self.assertEqual(self.b.current_playlist.tracks[2].name, 'e') + self.assertEqual(self.b.current_playlist.tracks[3].name, 'c') + self.assertEqual(self.b.current_playlist.tracks[4].name, 'd') + self.assertEqual(self.b.current_playlist.tracks[5].name, 'f') + self.assert_(u'OK' in result) + + def test_playlist_returns_same_as_playlistinfo(self): + playlist_result = self.h.handle_request(u'playlist') + playlistinfo_result = self.h.handle_request(u'playlistinfo') + self.assertEqual(playlist_result, playlistinfo_result) + + def test_playlistfind(self): + result = self.h.handle_request(u'playlistfind "tag" "needle"') + self.assert_(u'ACK [0@0] {} Not implemented' in result) + + def test_playlistfind_by_filename(self): + result = self.h.handle_request( + u'playlistfind "filename" "file:///dev/null"') + self.assert_(u'OK' in result) + + def test_playlistfind_by_filename_without_quotes(self): + result = self.h.handle_request( + u'playlistfind filename "file:///dev/null"') + self.assert_(u'OK' in result) + + def test_playlistfind_by_filename_in_current_playlist(self): + self.b.current_playlist.load([ + Track(uri='file:///exists')]) + result = self.h.handle_request( + u'playlistfind filename "file:///exists"') + self.assert_(u'file: file:///exists' in result) + self.assert_(u'OK' in result) + + def test_playlistid_without_songid(self): + self.b.current_playlist.load([Track(name='a'), Track(name='b')]) + result = self.h.handle_request(u'playlistid') + self.assert_(u'Title: a' in result) + self.assert_(u'Title: b' in result) + self.assert_(u'OK' in result) + + def test_playlistid_with_songid(self): + self.b.current_playlist.load([Track(name='a'), Track(name='b')]) + result = self.h.handle_request(u'playlistid "2"') + self.assert_(u'Title: a' not in result) + self.assert_(u'Id: 1' not in result) + self.assert_(u'Title: b' in result) + self.assert_(u'Id: 2' in result) + self.assert_(u'OK' in result) + + def test_playlistid_with_not_existing_songid_fails(self): + self.b.current_playlist.load([Track(name='a'), Track(name='b')]) + result = self.h.handle_request(u'playlistid "25"') + self.assertEqual(result[0], u'ACK [50@0] {playlistid} No such song') + + def test_playlistinfo_without_songpos_or_range(self): + self.b.current_playlist.load([ + Track(name='a'), Track(name='b'), Track(name='c'), + Track(name='d'), Track(name='e'), Track(name='f'), + ]) + result = self.h.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) + + def test_playlistinfo_with_songpos(self): + self.b.current_playlist.load([ + Track(name='a'), Track(name='b'), Track(name='c'), + Track(name='d'), Track(name='e'), Track(name='f'), + ]) + result = self.h.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) + + def test_playlistinfo_with_negative_songpos_same_as_playlistinfo(self): + result1 = self.h.handle_request(u'playlistinfo "-1"') + result2 = self.h.handle_request(u'playlistinfo') + self.assertEqual(result1, result2) + + def test_playlistinfo_with_open_range(self): + self.b.current_playlist.load([ + Track(name='a'), Track(name='b'), Track(name='c'), + Track(name='d'), Track(name='e'), Track(name='f'), + ]) + result = self.h.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) + + def test_playlistinfo_with_closed_range(self): + self.b.current_playlist.load([ + Track(name='a'), Track(name='b'), Track(name='c'), + Track(name='d'), Track(name='e'), Track(name='f'), + ]) + result = self.h.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) + + def test_playlistinfo_with_too_high_start_of_range_returns_arg_error(self): + result = self.h.handle_request(u'playlistinfo "10:20"') + self.assert_(u'ACK [2@0] {playlistinfo} Bad song index' in result) + + def test_playlistinfo_with_too_high_end_of_range_returns_ok(self): + result = self.h.handle_request(u'playlistinfo "0:20"') + self.assert_(u'OK' in result) + + def test_playlistsearch(self): + result = self.h.handle_request(u'playlistsearch "tag" "needle"') + self.assert_(u'ACK [0@0] {} Not implemented' in result) + + def test_plchanges(self): + self.b.current_playlist.load( + [Track(name='a'), Track(name='b'), Track(name='c')]) + result = self.h.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) + + def test_plchangesposid(self): + self.b.current_playlist.load( + [Track(id=11), Track(id=12), Track(id=13)]) + result = self.h.handle_request(u'plchangesposid "0"') + self.assert_(u'cpos: 0' in result) + self.assert_(u'Id: 11' in result) + self.assert_(u'cpos: 2' in result) + self.assert_(u'Id: 12' in result) + self.assert_(u'cpos: 2' in result) + self.assert_(u'Id: 13' in result) + self.assert_(u'OK' in result) + + def test_shuffle_without_range(self): + self.b.current_playlist.load([ + Track(name='a'), Track(name='b'), Track(name='c'), + Track(name='d'), Track(name='e'), Track(name='f'), + ]) + version = self.b.current_playlist.version + result = self.h.handle_request(u'shuffle') + self.assert_(version < self.b.current_playlist.version) + self.assert_(u'OK' in result) + + def test_shuffle_with_open_range(self): + self.b.current_playlist.load([ + Track(name='a'), Track(name='b'), Track(name='c'), + Track(name='d'), Track(name='e'), Track(name='f'), + ]) + version = self.b.current_playlist.version + result = self.h.handle_request(u'shuffle "4:"') + self.assert_(version < self.b.current_playlist.version) + self.assertEqual(self.b.current_playlist.tracks[0].name, 'a') + self.assertEqual(self.b.current_playlist.tracks[1].name, 'b') + self.assertEqual(self.b.current_playlist.tracks[2].name, 'c') + self.assertEqual(self.b.current_playlist.tracks[3].name, 'd') + self.assert_(u'OK' in result) + + def test_shuffle_with_closed_range(self): + self.b.current_playlist.load([ + Track(name='a'), Track(name='b'), Track(name='c'), + Track(name='d'), Track(name='e'), Track(name='f'), + ]) + version = self.b.current_playlist.version + result = self.h.handle_request(u'shuffle "1:3"') + self.assert_(version < self.b.current_playlist.version) + self.assertEqual(self.b.current_playlist.tracks[0].name, 'a') + self.assertEqual(self.b.current_playlist.tracks[3].name, 'd') + self.assertEqual(self.b.current_playlist.tracks[4].name, 'e') + self.assertEqual(self.b.current_playlist.tracks[5].name, 'f') + self.assert_(u'OK' in result) + + def test_swap(self): + self.b.current_playlist.load([ + Track(name='a'), Track(name='b'), Track(name='c'), + Track(name='d'), Track(name='e'), Track(name='f'), + ]) + result = self.h.handle_request(u'swap "1" "4"') + self.assertEqual(self.b.current_playlist.tracks[0].name, 'a') + self.assertEqual(self.b.current_playlist.tracks[1].name, 'e') + self.assertEqual(self.b.current_playlist.tracks[2].name, 'c') + self.assertEqual(self.b.current_playlist.tracks[3].name, 'd') + self.assertEqual(self.b.current_playlist.tracks[4].name, 'b') + self.assertEqual(self.b.current_playlist.tracks[5].name, 'f') + self.assert_(u'OK' in result) + + def test_swapid(self): + self.b.current_playlist.load([ + Track(name='a'), Track(name='b'), Track(name='c'), + Track(name='d'), Track(name='e'), Track(name='f'), + ]) + result = self.h.handle_request(u'swapid "2" "5"') + self.assertEqual(self.b.current_playlist.tracks[0].name, 'a') + self.assertEqual(self.b.current_playlist.tracks[1].name, 'e') + self.assertEqual(self.b.current_playlist.tracks[2].name, 'c') + self.assertEqual(self.b.current_playlist.tracks[3].name, 'd') + self.assertEqual(self.b.current_playlist.tracks[4].name, 'b') + self.assertEqual(self.b.current_playlist.tracks[5].name, 'f') + self.assert_(u'OK' in result) diff --git a/tests/mpd/frontend_test.py b/tests/mpd/frontend_test.py deleted file mode 100644 index 1e92d715..00000000 --- a/tests/mpd/frontend_test.py +++ /dev/null @@ -1,1274 +0,0 @@ -import datetime as dt -import unittest - -from mopidy.backends.dummy import DummyBackend -from mopidy.mixers.dummy import DummyMixer -from mopidy.models import Track, Playlist -from mopidy.mpd import frontend, MpdAckError - -from tests import SkipTest - -class RequestHandlerTest(unittest.TestCase): - def setUp(self): - self.m = DummyMixer() - self.b = DummyBackend(mixer=self.m) - self.h = frontend.MpdFrontend(backend=self.b) - - def test_register_same_pattern_twice_fails(self): - func = lambda: None - try: - frontend.handle_pattern('a pattern')(func) - frontend.handle_pattern('a pattern')(func) - self.fail('Registering a pattern twice shoulde raise ValueError') - except ValueError: - pass - - def test_finding_handler_for_unknown_command_raises_exception(self): - try: - self.h.find_handler('an_unknown_command with args') - fail('Should raise exception') - except MpdAckError as e: - self.assertEqual(e.get_mpd_ack(), - u'ACK [5@0] {} unknown command "an_unknown_command"') - - def test_finding_handler_for_known_command_returns_handler_and_kwargs(self): - expected_handler = lambda x: None - frontend._request_handlers['known_command (?P.+)'] = \ - expected_handler - (handler, kwargs) = self.h.find_handler('known_command an_arg') - self.assertEqual(handler, expected_handler) - self.assert_('arg1' in kwargs) - self.assertEqual(kwargs['arg1'], 'an_arg') - - def test_handling_unknown_request_yields_error(self): - result = self.h.handle_request('an unhandled request') - self.assertEqual(result[0], u'ACK [5@0] {} unknown command "an"') - - def test_handling_known_request(self): - expected = 'magic' - frontend._request_handlers['known request'] = lambda x: expected - result = self.h.handle_request('known request') - self.assert_(u'OK' in result) - self.assert_(expected in result) - - -class CommandListsTest(unittest.TestCase): - def setUp(self): - self.m = DummyMixer() - self.b = DummyBackend(mixer=self.m) - self.h = frontend.MpdFrontend(backend=self.b) - - def test_command_list_begin(self): - result = self.h.handle_request(u'command_list_begin') - self.assert_(result is None) - - def test_command_list_end(self): - self.h.handle_request(u'command_list_begin') - result = self.h.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.h.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.h.handle_request(u'command_list_begin') - self.assertEqual([], self.h.command_list) - self.assertEqual(False, self.h.command_list_ok) - self.h.handle_request(u'ping') - self.assert_(u'ping' in self.h.command_list) - result = self.h.handle_request(u'command_list_end') - self.assert_(u'OK' in result) - self.assertEqual(False, self.h.command_list) - - def test_command_list_with_error_returns_ack_with_correct_index(self): - self.h.handle_request(u'command_list_begin') - self.h.handle_request(u'play') # Known command - self.h.handle_request(u'paly') # Unknown command - result = self.h.handle_request(u'command_list_end') - self.assertEqual(result[0], u'ACK [5@1] {} unknown command "paly"') - - def test_command_list_ok_begin(self): - result = self.h.handle_request(u'command_list_ok_begin') - self.assert_(result is None) - - def test_command_list_ok_with_ping(self): - self.h.handle_request(u'command_list_ok_begin') - self.assertEqual([], self.h.command_list) - self.assertEqual(True, self.h.command_list_ok) - self.h.handle_request(u'ping') - self.assert_(u'ping' in self.h.command_list) - result = self.h.handle_request(u'command_list_end') - self.assert_(u'list_OK' in result) - self.assert_(u'OK' in result) - self.assertEqual(False, self.h.command_list) - self.assertEqual(False, self.h.command_list_ok) - - -class StatusHandlerTest(unittest.TestCase): - def setUp(self): - self.m = DummyMixer() - self.b = DummyBackend(mixer=self.m) - self.h = frontend.MpdFrontend(backend=self.b) - - def test_clearerror(self): - result = self.h.handle_request(u'clearerror') - self.assert_(u'ACK [0@0] {} Not implemented' in result) - - def test_currentsong(self): - track = Track() - self.b.current_playlist.load([track]) - self.b.playback.play() - result = self.h.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: 1' in result) - self.assert_(u'OK' in result) - - def test_currentsong_without_song(self): - result = self.h.handle_request(u'currentsong') - self.assert_(u'OK' in result) - - def test_idle_without_subsystems(self): - result = self.h.handle_request(u'idle') - self.assert_(u'OK' in result) - - def test_idle_with_subsystems(self): - result = self.h.handle_request(u'idle database playlist') - self.assert_(u'OK' in result) - - def test_noidle(self): - result = self.h.handle_request(u'noidle') - self.assert_(u'OK' in result) - - def test_stats_command(self): - result = self.h.handle_request(u'stats') - self.assert_(u'OK' in result) - - def test_stats_method(self): - result = self.h._status_stats() - self.assert_('artists' in result) - self.assert_(int(result['artists']) >= 0) - self.assert_('albums' in result) - self.assert_(int(result['albums']) >= 0) - self.assert_('songs' in result) - self.assert_(int(result['songs']) >= 0) - self.assert_('uptime' in result) - self.assert_(int(result['uptime']) >= 0) - self.assert_('db_playtime' in result) - self.assert_(int(result['db_playtime']) >= 0) - self.assert_('db_update' in result) - self.assert_(int(result['db_update']) >= 0) - self.assert_('playtime' in result) - self.assert_(int(result['playtime']) >= 0) - - def test_status_command(self): - result = self.h.handle_request(u'status') - self.assert_(u'OK' in result) - - def test_status_method_contains_volume_which_defaults_to_0(self): - result = dict(self.h._status_status()) - self.assert_('volume' in result) - self.assertEqual(int(result['volume']), 0) - - def test_status_method_contains_volume(self): - self.b.mixer.volume = 17 - result = dict(self.h._status_status()) - self.assert_('volume' in result) - self.assertEqual(int(result['volume']), 17) - - def test_status_method_contains_repeat_is_0(self): - result = dict(self.h._status_status()) - self.assert_('repeat' in result) - self.assertEqual(int(result['repeat']), 0) - - def test_status_method_contains_repeat_is_1(self): - self.b.playback.repeat = 1 - result = dict(self.h._status_status()) - self.assert_('repeat' in result) - self.assertEqual(int(result['repeat']), 1) - - def test_status_method_contains_random_is_0(self): - result = dict(self.h._status_status()) - self.assert_('random' in result) - self.assertEqual(int(result['random']), 0) - - def test_status_method_contains_random_is_1(self): - self.b.playback.random = 1 - result = dict(self.h._status_status()) - self.assert_('random' in result) - self.assertEqual(int(result['random']), 1) - - def test_status_method_contains_single(self): - result = dict(self.h._status_status()) - self.assert_('single' in result) - self.assert_(int(result['single']) in (0, 1)) - - def test_status_method_contains_consume_is_0(self): - result = dict(self.h._status_status()) - self.assert_('consume' in result) - self.assertEqual(int(result['consume']), 0) - - def test_status_method_contains_consume_is_1(self): - self.b.playback.consume = 1 - result = dict(self.h._status_status()) - self.assert_('consume' in result) - self.assertEqual(int(result['consume']), 1) - - def test_status_method_contains_playlist(self): - result = dict(self.h._status_status()) - self.assert_('playlist' in result) - self.assert_(int(result['playlist']) in xrange(0, 2**31 - 1)) - - def test_status_method_contains_playlistlength(self): - result = dict(self.h._status_status()) - self.assert_('playlistlength' in result) - self.assert_(int(result['playlistlength']) >= 0) - - def test_status_method_contains_xfade(self): - result = dict(self.h._status_status()) - self.assert_('xfade' in result) - self.assert_(int(result['xfade']) >= 0) - - def test_status_method_contains_state_is_play(self): - self.b.playback.state = self.b.playback.PLAYING - result = dict(self.h._status_status()) - self.assert_('state' in result) - self.assertEqual(result['state'], 'play') - - def test_status_method_contains_state_is_stop(self): - self.b.playback.state = self.b.playback.STOPPED - result = dict(self.h._status_status()) - self.assert_('state' in result) - self.assertEqual(result['state'], 'stop') - - def test_status_method_contains_state_is_pause(self): - self.b.playback.state = self.b.playback.PLAYING - self.b.playback.state = self.b.playback.PAUSED - result = dict(self.h._status_status()) - self.assert_('state' in result) - self.assertEqual(result['state'], 'pause') - - def test_status_method_when_playlist_loaded_contains_song(self): - self.b.current_playlist.load([Track()]) - self.b.playback.play() - result = dict(self.h._status_status()) - self.assert_('song' in result) - self.assert_(int(result['song']) >= 0) - - def test_status_method_when_playlist_loaded_contains_cpid_as_songid(self): - self.b.current_playlist.load([Track()]) - self.b.playback.play() - result = dict(self.h._status_status()) - self.assert_('songid' in result) - self.assertEqual(int(result['songid']), 1) - - def test_status_method_when_playing_contains_time_with_no_length(self): - self.b.current_playlist.load([Track(length=None)]) - self.b.playback.play() - result = dict(self.h._status_status()) - self.assert_('time' in result) - (position, total) = result['time'].split(':') - position = int(position) - total = int(total) - self.assert_(position <= total) - - def test_status_method_when_playing_contains_time_with_length(self): - self.b.current_playlist.load([Track(length=10000)]) - self.b.playback.play() - result = dict(self.h._status_status()) - self.assert_('time' in result) - (position, total) = result['time'].split(':') - position = int(position) - total = int(total) - self.assert_(position <= total) - - def test_status_method_when_playing_contains_elapsed(self): - self.b.playback.state = self.b.playback.PAUSED - self.b.playback._play_time_accumulated = 59123 - result = dict(self.h._status_status()) - self.assert_('elapsed' in result) - self.assertEqual(int(result['elapsed']), 59123) - - def test_status_method_when_playing_contains_bitrate(self): - self.b.current_playlist.load([Track(bitrate=320)]) - self.b.playback.play() - result = dict(self.h._status_status()) - self.assert_('bitrate' in result) - self.assertEqual(int(result['bitrate']), 320) - - -class PlaybackOptionsHandlerTest(unittest.TestCase): - def setUp(self): - self.m = DummyMixer() - self.b = DummyBackend(mixer=self.m) - self.h = frontend.MpdFrontend(backend=self.b) - - def test_consume_off(self): - result = self.h.handle_request(u'consume "0"') - self.assertFalse(self.b.playback.consume) - self.assert_(u'OK' in result) - - def test_consume_on(self): - result = self.h.handle_request(u'consume "1"') - self.assertTrue(self.b.playback.consume) - self.assert_(u'OK' in result) - - def test_crossfade(self): - result = self.h.handle_request(u'crossfade "10"') - self.assert_(u'ACK [0@0] {} Not implemented' in result) - - def test_random_off(self): - result = self.h.handle_request(u'random "0"') - self.assertFalse(self.b.playback.random) - self.assert_(u'OK' in result) - - def test_random_on(self): - result = self.h.handle_request(u'random "1"') - self.assertTrue(self.b.playback.random) - self.assert_(u'OK' in result) - - def test_repeat_off(self): - result = self.h.handle_request(u'repeat "0"') - self.assertFalse(self.b.playback.repeat) - self.assert_(u'OK' in result) - - def test_repeat_on(self): - result = self.h.handle_request(u'repeat "1"') - self.assertTrue(self.b.playback.repeat) - self.assert_(u'OK' in result) - - def test_setvol_below_min(self): - result = self.h.handle_request(u'setvol "-10"') - self.assert_(u'OK' in result) - self.assertEqual(0, self.b.mixer.volume) - - def test_setvol_min(self): - result = self.h.handle_request(u'setvol "0"') - self.assert_(u'OK' in result) - self.assertEqual(0, self.b.mixer.volume) - - def test_setvol_middle(self): - result = self.h.handle_request(u'setvol "50"') - self.assert_(u'OK' in result) - self.assertEqual(50, self.b.mixer.volume) - - def test_setvol_max(self): - result = self.h.handle_request(u'setvol "100"') - self.assert_(u'OK' in result) - self.assertEqual(100, self.b.mixer.volume) - - def test_setvol_above_max(self): - result = self.h.handle_request(u'setvol "110"') - self.assert_(u'OK' in result) - self.assertEqual(100, self.b.mixer.volume) - - def test_setvol_plus_is_ignored(self): - result = self.h.handle_request(u'setvol "+10"') - self.assert_(u'OK' in result) - self.assertEqual(10, self.b.mixer.volume) - - def test_single_off(self): - result = self.h.handle_request(u'single "0"') - self.assertFalse(self.b.playback.single) - self.assert_(u'OK' in result) - - def test_single_on(self): - result = self.h.handle_request(u'single "1"') - self.assertTrue(self.b.playback.single) - self.assert_(u'OK' in result) - - def test_replay_gain_mode_off(self): - result = self.h.handle_request(u'replay_gain_mode "off"') - self.assert_(u'ACK [0@0] {} Not implemented' in result) - - def test_replay_gain_mode_track(self): - result = self.h.handle_request(u'replay_gain_mode "track"') - self.assert_(u'ACK [0@0] {} Not implemented' in result) - - def test_replay_gain_mode_album(self): - result = self.h.handle_request(u'replay_gain_mode "album"') - self.assert_(u'ACK [0@0] {} Not implemented' in result) - - def test_replay_gain_status_default(self): - expected = u'off' - result = self.h.handle_request(u'replay_gain_status') - self.assert_(u'OK' in result) - self.assert_(expected in result) - - #def test_replay_gain_status_off(self): - # expected = u'off' - # self.h._replay_gain_mode(expected) - # result = self.h.handle_request(u'replay_gain_status') - # self.assert_(u'OK' in result) - # self.assert_(expected in result) - - #def test_replay_gain_status_track(self): - # expected = u'track' - # self.h._replay_gain_mode(expected) - # result = self.h.handle_request(u'replay_gain_status') - # self.assert_(u'OK' in result) - # self.assert_(expected in result) - - #def test_replay_gain_status_album(self): - # expected = u'album' - # self.h._replay_gain_mode(expected) - # result = self.h.handle_request(u'replay_gain_status') - # self.assert_(u'OK' in result) - # self.assert_(expected in result) - - -class PlaybackControlHandlerTest(unittest.TestCase): - def setUp(self): - self.m = DummyMixer() - self.b = DummyBackend(mixer=self.m) - self.h = frontend.MpdFrontend(backend=self.b) - - def test_next(self): - result = self.h.handle_request(u'next') - self.assert_(u'OK' in result) - - def test_pause_off(self): - track = Track() - self.b.current_playlist.load([track]) - self.h.handle_request(u'play "0"') - self.h.handle_request(u'pause "1"') - result = self.h.handle_request(u'pause "0"') - self.assert_(u'OK' in result) - self.assertEqual(self.b.playback.PLAYING, self.b.playback.state) - - def test_pause_on(self): - track = Track() - self.b.current_playlist.load([track]) - self.h.handle_request(u'play "0"') - result = self.h.handle_request(u'pause "1"') - self.assert_(u'OK' in result) - self.assertEqual(self.b.playback.PAUSED, self.b.playback.state) - - def test_play_without_pos(self): - track = Track() - self.b.current_playlist.load([track]) - self.b.playback.state = self.b.playback.PAUSED - result = self.h.handle_request(u'play') - self.assert_(u'OK' in result) - self.assertEqual(self.b.playback.PLAYING, self.b.playback.state) - - def test_play_with_pos(self): - self.b.current_playlist.load([Track()]) - result = self.h.handle_request(u'play "0"') - self.assert_(u'OK' in result) - self.assertEqual(self.b.playback.PLAYING, self.b.playback.state) - - def test_play_with_pos_out_of_bounds(self): - self.b.current_playlist.load([]) - result = self.h.handle_request(u'play "0"') - self.assertEqual(result[0], u'ACK [2@0] {play} Bad song index') - self.assertEqual(self.b.playback.STOPPED, self.b.playback.state) - - def test_play_minus_one_plays_first_in_playlist(self): - track = Track() - self.b.current_playlist.load([track]) - result = self.h.handle_request(u'play "-1"') - self.assert_(u'OK' in result) - self.assertEqual(self.b.playback.PLAYING, self.b.playback.state) - self.assertEqual(self.b.playback.current_track, track) - - def test_playid(self): - self.b.current_playlist.load([Track()]) - result = self.h.handle_request(u'playid "1"') - self.assert_(u'OK' in result) - self.assertEqual(self.b.playback.PLAYING, self.b.playback.state) - - def test_playid_minus_one_plays_first_in_playlist(self): - track = Track() - self.b.current_playlist.load([track]) - result = self.h.handle_request(u'playid "-1"') - self.assert_(u'OK' in result) - self.assertEqual(self.b.playback.PLAYING, self.b.playback.state) - self.assertEqual(self.b.playback.current_track, track) - - def test_playid_which_does_not_exist(self): - self.b.current_playlist.load([Track()]) - result = self.h.handle_request(u'playid "12345"') - self.assertEqual(result[0], u'ACK [50@0] {playid} No such song') - - def test_previous(self): - result = self.h.handle_request(u'previous') - self.assert_(u'OK' in result) - - def test_seek(self): - result = self.h.handle_request(u'seek "0" "30"') - self.assert_(u'ACK [0@0] {} Not implemented' in result) - - def test_seekid(self): - result = self.h.handle_request(u'seekid "0" "30"') - self.assert_(u'ACK [0@0] {} Not implemented' in result) - - def test_stop(self): - result = self.h.handle_request(u'stop') - self.assert_(u'OK' in result) - self.assertEqual(self.b.playback.STOPPED, self.b.playback.state) - - -class CurrentPlaylistHandlerTest(unittest.TestCase): - def setUp(self): - self.m = DummyMixer() - self.b = DummyBackend(mixer=self.m) - self.h = frontend.MpdFrontend(backend=self.b) - - def test_add(self): - needle = Track(uri='dummy://foo') - self.b.library._library = [Track(), Track(), needle, Track()] - self.b.current_playlist.load( - [Track(), Track(), Track(), Track(), Track()]) - self.assertEqual(len(self.b.current_playlist.tracks), 5) - result = self.h.handle_request(u'add "dummy://foo"') - self.assertEqual(len(self.b.current_playlist.tracks), 6) - self.assertEqual(self.b.current_playlist.tracks[5], needle) - self.assertEqual(len(result), 1) - self.assert_(u'OK' in result) - - def test_add_with_uri_not_found_in_library_should_ack(self): - result = self.h.handle_request(u'add "dummy://foo"') - self.assertEqual(result[0], - u'ACK [50@0] {add} directory or file not found') - - def test_addid_without_songpos(self): - needle = Track(uri='dummy://foo', id=137) - self.b.library._library = [Track(), Track(), needle, Track()] - self.b.current_playlist.load( - [Track(), Track(), Track(), Track(), Track()]) - self.assertEqual(len(self.b.current_playlist.tracks), 5) - result = self.h.handle_request(u'addid "dummy://foo"') - self.assertEqual(len(self.b.current_playlist.tracks), 6) - self.assertEqual(self.b.current_playlist.tracks[5], needle) - self.assert_(u'Id: 137' in result) - self.assert_(u'OK' in result) - - def test_addid_with_songpos(self): - needle = Track(uri='dummy://foo', id=137) - self.b.library._library = [Track(), Track(), needle, Track()] - self.b.current_playlist.load( - [Track(), Track(), Track(), Track(), Track()]) - self.assertEqual(len(self.b.current_playlist.tracks), 5) - result = self.h.handle_request(u'addid "dummy://foo" "3"') - self.assertEqual(len(self.b.current_playlist.tracks), 6) - self.assertEqual(self.b.current_playlist.tracks[3], needle) - self.assert_(u'Id: 137' in result) - self.assert_(u'OK' in result) - - def test_addid_with_songpos_out_of_bounds_should_ack(self): - needle = Track(uri='dummy://foo', id=137) - self.b.library._library = [Track(), Track(), needle, Track()] - self.b.current_playlist.load( - [Track(), Track(), Track(), Track(), Track()]) - self.assertEqual(len(self.b.current_playlist.tracks), 5) - result = self.h.handle_request(u'addid "dummy://foo" "6"') - self.assertEqual(result[0], u'ACK [2@0] {addid} Bad song index') - - def test_addid_with_uri_not_found_in_library_should_ack(self): - result = self.h.handle_request(u'addid "dummy://foo"') - self.assertEqual(result[0], u'ACK [50@0] {addid} No such song') - - def test_clear(self): - self.b.current_playlist.load( - [Track(), Track(), Track(), Track(), Track()]) - self.assertEqual(len(self.b.current_playlist.tracks), 5) - result = self.h.handle_request(u'clear') - self.assertEqual(len(self.b.current_playlist.tracks), 0) - self.assertEqual(self.b.playback.current_track, None) - self.assert_(u'OK' in result) - - def test_delete_songpos(self): - self.b.current_playlist.load( - [Track(id=1), Track(id=2), Track(id=3), Track(id=4), Track(id=5)]) - self.assertEqual(len(self.b.current_playlist.tracks), 5) - result = self.h.handle_request(u'delete "2"') - self.assertEqual(len(self.b.current_playlist.tracks), 4) - self.assert_(u'OK' in result) - - def test_delete_songpos_out_of_bounds(self): - self.b.current_playlist.load( - [Track(), Track(), Track(), Track(), Track()]) - self.assertEqual(len(self.b.current_playlist.tracks), 5) - result = self.h.handle_request(u'delete "5"') - self.assertEqual(len(self.b.current_playlist.tracks), 5) - self.assertEqual(result[0], u'ACK [2@0] {delete} Bad song index') - - def test_delete_open_range(self): - self.b.current_playlist.load( - [Track(id=1), Track(id=2), Track(id=3), Track(id=4), Track(id=5)]) - self.assertEqual(len(self.b.current_playlist.tracks), 5) - result = self.h.handle_request(u'delete "1:"') - self.assertEqual(len(self.b.current_playlist.tracks), 1) - self.assert_(u'OK' in result) - - def test_delete_closed_range(self): - self.b.current_playlist.load( - [Track(id=1), Track(id=2), Track(id=3), Track(id=4), Track(id=5)]) - self.assertEqual(len(self.b.current_playlist.tracks), 5) - result = self.h.handle_request(u'delete "1:3"') - self.assertEqual(len(self.b.current_playlist.tracks), 3) - self.assert_(u'OK' in result) - - def test_delete_range_out_of_bounds(self): - self.b.current_playlist.load( - [Track(), Track(), Track(), Track(), Track()]) - self.assertEqual(len(self.b.current_playlist.tracks), 5) - result = self.h.handle_request(u'delete "5:7"') - self.assertEqual(len(self.b.current_playlist.tracks), 5) - self.assertEqual(result[0], u'ACK [2@0] {delete} Bad song index') - - def test_deleteid(self): - self.b.current_playlist.load([Track(), Track()]) - self.assertEqual(len(self.b.current_playlist.tracks), 2) - result = self.h.handle_request(u'deleteid "2"') - self.assertEqual(len(self.b.current_playlist.tracks), 1) - self.assert_(u'OK' in result) - - def test_deleteid_does_not_exist(self): - self.b.current_playlist.load([Track(), Track()]) - self.assertEqual(len(self.b.current_playlist.tracks), 2) - result = self.h.handle_request(u'deleteid "12345"') - self.assertEqual(len(self.b.current_playlist.tracks), 2) - self.assertEqual(result[0], u'ACK [50@0] {deleteid} No such song') - - def test_move_songpos(self): - self.b.current_playlist.load([ - Track(name='a'), Track(name='b'), Track(name='c'), - Track(name='d'), Track(name='e'), Track(name='f'), - ]) - result = self.h.handle_request(u'move "1" "0"') - self.assertEqual(self.b.current_playlist.tracks[0].name, 'b') - self.assertEqual(self.b.current_playlist.tracks[1].name, 'a') - self.assertEqual(self.b.current_playlist.tracks[2].name, 'c') - self.assertEqual(self.b.current_playlist.tracks[3].name, 'd') - self.assertEqual(self.b.current_playlist.tracks[4].name, 'e') - self.assertEqual(self.b.current_playlist.tracks[5].name, 'f') - self.assert_(u'OK' in result) - - def test_move_open_range(self): - self.b.current_playlist.load([ - Track(name='a'), Track(name='b'), Track(name='c'), - Track(name='d'), Track(name='e'), Track(name='f'), - ]) - result = self.h.handle_request(u'move "2:" "0"') - self.assertEqual(self.b.current_playlist.tracks[0].name, 'c') - self.assertEqual(self.b.current_playlist.tracks[1].name, 'd') - self.assertEqual(self.b.current_playlist.tracks[2].name, 'e') - self.assertEqual(self.b.current_playlist.tracks[3].name, 'f') - self.assertEqual(self.b.current_playlist.tracks[4].name, 'a') - self.assertEqual(self.b.current_playlist.tracks[5].name, 'b') - self.assert_(u'OK' in result) - - def test_move_closed_range(self): - self.b.current_playlist.load([ - Track(name='a'), Track(name='b'), Track(name='c'), - Track(name='d'), Track(name='e'), Track(name='f'), - ]) - result = self.h.handle_request(u'move "1:3" "0"') - self.assertEqual(self.b.current_playlist.tracks[0].name, 'b') - self.assertEqual(self.b.current_playlist.tracks[1].name, 'c') - self.assertEqual(self.b.current_playlist.tracks[2].name, 'a') - self.assertEqual(self.b.current_playlist.tracks[3].name, 'd') - self.assertEqual(self.b.current_playlist.tracks[4].name, 'e') - self.assertEqual(self.b.current_playlist.tracks[5].name, 'f') - self.assert_(u'OK' in result) - - def test_moveid(self): - self.b.current_playlist.load([ - Track(name='a'), Track(name='b'), Track(name='c'), - Track(name='d'), Track(name='e'), Track(name='f'), - ]) - result = self.h.handle_request(u'moveid "5" "2"') - self.assertEqual(self.b.current_playlist.tracks[0].name, 'a') - self.assertEqual(self.b.current_playlist.tracks[1].name, 'b') - self.assertEqual(self.b.current_playlist.tracks[2].name, 'e') - self.assertEqual(self.b.current_playlist.tracks[3].name, 'c') - self.assertEqual(self.b.current_playlist.tracks[4].name, 'd') - self.assertEqual(self.b.current_playlist.tracks[5].name, 'f') - self.assert_(u'OK' in result) - - def test_playlist_returns_same_as_playlistinfo(self): - playlist_result = self.h.handle_request(u'playlist') - playlistinfo_result = self.h.handle_request(u'playlistinfo') - self.assertEqual(playlist_result, playlistinfo_result) - - def test_playlistfind(self): - result = self.h.handle_request(u'playlistfind "tag" "needle"') - self.assert_(u'ACK [0@0] {} Not implemented' in result) - - def test_playlistfind_by_filename(self): - result = self.h.handle_request( - u'playlistfind "filename" "file:///dev/null"') - self.assert_(u'OK' in result) - - def test_playlistfind_by_filename_without_quotes(self): - result = self.h.handle_request( - u'playlistfind filename "file:///dev/null"') - self.assert_(u'OK' in result) - - def test_playlistfind_by_filename_in_current_playlist(self): - self.b.current_playlist.load([ - Track(uri='file:///exists')]) - result = self.h.handle_request( - u'playlistfind filename "file:///exists"') - self.assert_(u'file: file:///exists' in result) - self.assert_(u'OK' in result) - - def test_playlistid_without_songid(self): - self.b.current_playlist.load([Track(name='a'), Track(name='b')]) - result = self.h.handle_request(u'playlistid') - self.assert_(u'Title: a' in result) - self.assert_(u'Title: b' in result) - self.assert_(u'OK' in result) - - def test_playlistid_with_songid(self): - self.b.current_playlist.load([Track(name='a'), Track(name='b')]) - result = self.h.handle_request(u'playlistid "2"') - self.assert_(u'Title: a' not in result) - self.assert_(u'Id: 1' not in result) - self.assert_(u'Title: b' in result) - self.assert_(u'Id: 2' in result) - self.assert_(u'OK' in result) - - def test_playlistid_with_not_existing_songid_fails(self): - self.b.current_playlist.load([Track(name='a'), Track(name='b')]) - result = self.h.handle_request(u'playlistid "25"') - self.assertEqual(result[0], u'ACK [50@0] {playlistid} No such song') - - def test_playlistinfo_without_songpos_or_range(self): - self.b.current_playlist.load([ - Track(name='a'), Track(name='b'), Track(name='c'), - Track(name='d'), Track(name='e'), Track(name='f'), - ]) - result = self.h.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) - - def test_playlistinfo_with_songpos(self): - self.b.current_playlist.load([ - Track(name='a'), Track(name='b'), Track(name='c'), - Track(name='d'), Track(name='e'), Track(name='f'), - ]) - result = self.h.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) - - def test_playlistinfo_with_negative_songpos_same_as_playlistinfo(self): - result1 = self.h.handle_request(u'playlistinfo "-1"') - result2 = self.h.handle_request(u'playlistinfo') - self.assertEqual(result1, result2) - - def test_playlistinfo_with_open_range(self): - self.b.current_playlist.load([ - Track(name='a'), Track(name='b'), Track(name='c'), - Track(name='d'), Track(name='e'), Track(name='f'), - ]) - result = self.h.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) - - def test_playlistinfo_with_closed_range(self): - self.b.current_playlist.load([ - Track(name='a'), Track(name='b'), Track(name='c'), - Track(name='d'), Track(name='e'), Track(name='f'), - ]) - result = self.h.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) - - def test_playlistinfo_with_too_high_start_of_range_returns_arg_error(self): - result = self.h.handle_request(u'playlistinfo "10:20"') - self.assert_(u'ACK [2@0] {playlistinfo} Bad song index' in result) - - def test_playlistinfo_with_too_high_end_of_range_returns_ok(self): - result = self.h.handle_request(u'playlistinfo "0:20"') - self.assert_(u'OK' in result) - - def test_playlistsearch(self): - result = self.h.handle_request(u'playlistsearch "tag" "needle"') - self.assert_(u'ACK [0@0] {} Not implemented' in result) - - def test_plchanges(self): - self.b.current_playlist.load( - [Track(name='a'), Track(name='b'), Track(name='c')]) - result = self.h.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) - - def test_plchangesposid(self): - self.b.current_playlist.load( - [Track(id=11), Track(id=12), Track(id=13)]) - result = self.h.handle_request(u'plchangesposid "0"') - self.assert_(u'cpos: 0' in result) - self.assert_(u'Id: 11' in result) - self.assert_(u'cpos: 2' in result) - self.assert_(u'Id: 12' in result) - self.assert_(u'cpos: 2' in result) - self.assert_(u'Id: 13' in result) - self.assert_(u'OK' in result) - - def test_shuffle_without_range(self): - self.b.current_playlist.load([ - Track(name='a'), Track(name='b'), Track(name='c'), - Track(name='d'), Track(name='e'), Track(name='f'), - ]) - version = self.b.current_playlist.version - result = self.h.handle_request(u'shuffle') - self.assert_(version < self.b.current_playlist.version) - self.assert_(u'OK' in result) - - def test_shuffle_with_open_range(self): - self.b.current_playlist.load([ - Track(name='a'), Track(name='b'), Track(name='c'), - Track(name='d'), Track(name='e'), Track(name='f'), - ]) - version = self.b.current_playlist.version - result = self.h.handle_request(u'shuffle "4:"') - self.assert_(version < self.b.current_playlist.version) - self.assertEqual(self.b.current_playlist.tracks[0].name, 'a') - self.assertEqual(self.b.current_playlist.tracks[1].name, 'b') - self.assertEqual(self.b.current_playlist.tracks[2].name, 'c') - self.assertEqual(self.b.current_playlist.tracks[3].name, 'd') - self.assert_(u'OK' in result) - - def test_shuffle_with_closed_range(self): - self.b.current_playlist.load([ - Track(name='a'), Track(name='b'), Track(name='c'), - Track(name='d'), Track(name='e'), Track(name='f'), - ]) - version = self.b.current_playlist.version - result = self.h.handle_request(u'shuffle "1:3"') - self.assert_(version < self.b.current_playlist.version) - self.assertEqual(self.b.current_playlist.tracks[0].name, 'a') - self.assertEqual(self.b.current_playlist.tracks[3].name, 'd') - self.assertEqual(self.b.current_playlist.tracks[4].name, 'e') - self.assertEqual(self.b.current_playlist.tracks[5].name, 'f') - self.assert_(u'OK' in result) - - def test_swap(self): - self.b.current_playlist.load([ - Track(name='a'), Track(name='b'), Track(name='c'), - Track(name='d'), Track(name='e'), Track(name='f'), - ]) - result = self.h.handle_request(u'swap "1" "4"') - self.assertEqual(self.b.current_playlist.tracks[0].name, 'a') - self.assertEqual(self.b.current_playlist.tracks[1].name, 'e') - self.assertEqual(self.b.current_playlist.tracks[2].name, 'c') - self.assertEqual(self.b.current_playlist.tracks[3].name, 'd') - self.assertEqual(self.b.current_playlist.tracks[4].name, 'b') - self.assertEqual(self.b.current_playlist.tracks[5].name, 'f') - self.assert_(u'OK' in result) - - def test_swapid(self): - self.b.current_playlist.load([ - Track(name='a'), Track(name='b'), Track(name='c'), - Track(name='d'), Track(name='e'), Track(name='f'), - ]) - result = self.h.handle_request(u'swapid "2" "5"') - self.assertEqual(self.b.current_playlist.tracks[0].name, 'a') - self.assertEqual(self.b.current_playlist.tracks[1].name, 'e') - self.assertEqual(self.b.current_playlist.tracks[2].name, 'c') - self.assertEqual(self.b.current_playlist.tracks[3].name, 'd') - self.assertEqual(self.b.current_playlist.tracks[4].name, 'b') - self.assertEqual(self.b.current_playlist.tracks[5].name, 'f') - self.assert_(u'OK' in result) - - -class StoredPlaylistsHandlerTest(unittest.TestCase): - def setUp(self): - self.m = DummyMixer() - self.b = DummyBackend(mixer=self.m) - self.h = frontend.MpdFrontend(backend=self.b) - - def test_listplaylist(self): - self.b.stored_playlists.playlists = [ - Playlist(name='name', tracks=[Track(uri='file:///dev/urandom')])] - result = self.h.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.h.handle_request(u'listplaylist "name"') - self.assertEqual(result[0], - u'ACK [50@0] {listplaylist} No such playlist') - - def test_listplaylistinfo(self): - self.b.stored_playlists.playlists = [ - Playlist(name='name', tracks=[Track(uri='file:///dev/urandom')])] - result = self.h.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.h.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.b.stored_playlists.playlists = [Playlist(name='a', - last_modified=last_modified)] - result = self.h.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(self): - result = self.h.handle_request(u'load "name"') - self.assert_(u'OK' in result) - - def test_load_appends(self): - raise SkipTest - - def test_playlistadd(self): - result = self.h.handle_request( - u'playlistadd "name" "file:///dev/urandom"') - self.assert_(u'ACK [0@0] {} Not implemented' in result) - - def test_playlistclear(self): - result = self.h.handle_request(u'playlistclear "name"') - self.assert_(u'ACK [0@0] {} Not implemented' in result) - - def test_playlistdelete(self): - result = self.h.handle_request(u'playlistdelete "name" "5"') - self.assert_(u'ACK [0@0] {} Not implemented' in result) - - def test_playlistmove(self): - result = self.h.handle_request(u'playlistmove "name" "5" "10"') - self.assert_(u'ACK [0@0] {} Not implemented' in result) - - def test_rename(self): - result = self.h.handle_request(u'rename "old_name" "new_name"') - self.assert_(u'ACK [0@0] {} Not implemented' in result) - - def test_rm(self): - result = self.h.handle_request(u'rm "name"') - self.assert_(u'ACK [0@0] {} Not implemented' in result) - - def test_save(self): - result = self.h.handle_request(u'save "name"') - self.assert_(u'ACK [0@0] {} Not implemented' in result) - - -class MusicDatabaseHandlerTest(unittest.TestCase): - def setUp(self): - self.m = DummyMixer() - self.b = DummyBackend(mixer=self.m) - self.h = frontend.MpdFrontend(backend=self.b) - - def test_count(self): - result = self.h.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_find_album(self): - result = self.h.handle_request(u'find "album" "what"') - self.assert_(u'OK' in result) - - def test_find_album_without_quotes(self): - result = self.h.handle_request(u'find album "what"') - self.assert_(u'OK' in result) - - def test_find_artist(self): - result = self.h.handle_request(u'find "artist" "what"') - self.assert_(u'OK' in result) - - def test_find_artist_without_quotes(self): - result = self.h.handle_request(u'find artist "what"') - self.assert_(u'OK' in result) - - def test_find_title(self): - result = self.h.handle_request(u'find "title" "what"') - self.assert_(u'OK' in result) - - def test_find_title_without_quotes(self): - result = self.h.handle_request(u'find title "what"') - self.assert_(u'OK' in result) - - def test_find_else_should_fail(self): - result = self.h.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.h.handle_request(u'find album "album_what" artist "artist_what"') - self.assert_(u'OK' in result) - - def test_findadd(self): - result = self.h.handle_request(u'findadd "album" "what"') - self.assert_(u'OK' in result) - - def test_list_artist(self): - result = self.h.handle_request(u'list "artist"') - self.assert_(u'OK' in result) - - def test_list_artist_without_quotes(self): - result = self.h.handle_request(u'list artist') - self.assert_(u'OK' in result) - - def test_list_artist_without_quotes_and_capitalized(self): - result = self.h.handle_request(u'list Artist') - self.assert_(u'OK' in result) - - def test_list_artist_with_artist_should_fail(self): - result = self.h.handle_request(u'list "artist" "anartist"') - self.assertEqual(result[0], u'ACK [2@0] {list} incorrect arguments') - - def test_list_album_without_artist(self): - result = self.h.handle_request(u'list "album"') - self.assert_(u'OK' in result) - - def test_list_album_with_artist(self): - result = self.h.handle_request(u'list "album" "anartist"') - self.assert_(u'OK' in result) - - def test_list_album_artist_with_artist_without_quotes(self): - result = self.h.handle_request(u'list album artist "anartist"') - self.assert_(u'OK' in result) - - def test_listall(self): - result = self.h.handle_request(u'listall "file:///dev/urandom"') - self.assert_(u'ACK [0@0] {} Not implemented' in result) - - def test_listallinfo(self): - result = self.h.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.h.handle_request(u'lsinfo') - listplaylists_result = self.h.handle_request(u'listplaylists') - self.assertEqual(lsinfo_result, listplaylists_result) - - def test_lsinfo_with_empty_path_returns_same_as_listplaylists(self): - lsinfo_result = self.h.handle_request(u'lsinfo ""') - listplaylists_result = self.h.handle_request(u'listplaylists') - self.assertEqual(lsinfo_result, listplaylists_result) - - def test_lsinfo_for_root_returns_same_as_listplaylists(self): - lsinfo_result = self.h.handle_request(u'lsinfo "/"') - listplaylists_result = self.h.handle_request(u'listplaylists') - self.assertEqual(lsinfo_result, listplaylists_result) - - def test_search_album(self): - result = self.h.handle_request(u'search "album" "analbum"') - self.assert_(u'OK' in result) - - def test_search_album_without_quotes(self): - result = self.h.handle_request(u'search album "analbum"') - self.assert_(u'OK' in result) - - def test_search_artist(self): - result = self.h.handle_request(u'search "artist" "anartist"') - self.assert_(u'OK' in result) - - def test_search_artist_without_quotes(self): - result = self.h.handle_request(u'search artist "anartist"') - self.assert_(u'OK' in result) - - def test_search_filename(self): - result = self.h.handle_request(u'search "filename" "afilename"') - self.assert_(u'OK' in result) - - def test_search_filename_without_quotes(self): - result = self.h.handle_request(u'search filename "afilename"') - self.assert_(u'OK' in result) - - def test_search_title(self): - result = self.h.handle_request(u'search "title" "atitle"') - self.assert_(u'OK' in result) - - def test_search_title_without_quotes(self): - result = self.h.handle_request(u'search title "atitle"') - self.assert_(u'OK' in result) - - def test_search_any(self): - result = self.h.handle_request(u'search "any" "anything"') - self.assert_(u'OK' in result) - - def test_search_any_without_quotes(self): - result = self.h.handle_request(u'search any "anything"') - self.assert_(u'OK' in result) - - def test_search_else_should_fail(self): - result = self.h.handle_request(u'search "sometype" "something"') - self.assertEqual(result[0], u'ACK [2@0] {search} incorrect arguments') - - def test_update_without_uri(self): - result = self.h.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.h.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.h.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.h.handle_request(u'rescan "file:///dev/urandom"') - self.assert_(u'OK' in result) - self.assert_(u'updating_db: 0' in result) - - -class StickersHandlerTest(unittest.TestCase): - def setUp(self): - self.m = DummyMixer() - self.b = DummyBackend(mixer=self.m) - self.h = frontend.MpdFrontend(backend=self.b) - - def test_sticker_get(self): - result = self.h.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.h.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.h.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.h.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.h.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.h.handle_request( - u'sticker find "song" "file:///dev/urandom" "a_name"') - self.assert_(u'ACK [0@0] {} Not implemented' in result) - - -class ConnectionHandlerTest(unittest.TestCase): - def setUp(self): - self.m = DummyMixer() - self.b = DummyBackend(mixer=self.m) - self.h = frontend.MpdFrontend(backend=self.b) - - def test_close(self): - result = self.h.handle_request(u'close') - self.assert_(u'OK' in result) - - def test_empty_request(self): - result = self.h.handle_request(u'') - self.assert_(u'OK' in result) - - def test_kill(self): - result = self.h.handle_request(u'kill') - self.assert_(u'OK' in result) - - def test_password(self): - result = self.h.handle_request(u'password "secret"') - self.assert_(u'ACK [0@0] {} Not implemented' in result) - - def test_ping(self): - result = self.h.handle_request(u'ping') - self.assert_(u'OK' in result) - - -class AudioOutputHandlerTest(unittest.TestCase): - def setUp(self): - self.m = DummyMixer() - self.b = DummyBackend(mixer=self.m) - self.h = frontend.MpdFrontend(backend=self.b) - - def test_enableoutput(self): - result = self.h.handle_request(u'enableoutput "0"') - self.assert_(u'ACK [0@0] {} Not implemented' in result) - - def test_disableoutput(self): - result = self.h.handle_request(u'disableoutput "0"') - self.assert_(u'ACK [0@0] {} Not implemented' in result) - - def test_outputs(self): - result = self.h.handle_request(u'outputs') - self.assert_(u'outputid: 0' in result) - self.assert_(u'outputname: DummyBackend' in result) - self.assert_(u'outputenabled: 1' in result) - self.assert_(u'OK' in result) - - -class ReflectionHandlerTest(unittest.TestCase): - def setUp(self): - self.m = DummyMixer() - self.b = DummyBackend(mixer=self.m) - self.h = frontend.MpdFrontend(backend=self.b) - - def test_commands_returns_list_of_all_commands(self): - result = self.h.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 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_decoders(self): - result = self.h.handle_request(u'decoders') - self.assert_(u'ACK [0@0] {} Not implemented' in result) - - def test_notcommands_returns_only_ok(self): - result = self.h.handle_request(u'notcommands') - self.assertEqual(1, len(result)) - self.assert_(u'OK' in result) - - def test_tagtypes(self): - result = self.h.handle_request(u'tagtypes') - self.assert_(u'OK' in result) - - def test_urlhandlers(self): - result = self.h.handle_request(u'urlhandlers') - self.assert_(u'OK' in result) - self.assert_(u'handler: dummy:' in result) diff --git a/tests/mpd/music_db_test.py b/tests/mpd/music_db_test.py new file mode 100644 index 00000000..5256ada4 --- /dev/null +++ b/tests/mpd/music_db_test.py @@ -0,0 +1,168 @@ +import unittest + +from mopidy.backends.dummy import DummyBackend +from mopidy.mixers.dummy import DummyMixer +from mopidy.mpd import frontend + +class MusicDatabaseHandlerTest(unittest.TestCase): + def setUp(self): + self.m = DummyMixer() + self.b = DummyBackend(mixer=self.m) + self.h = frontend.MpdFrontend(backend=self.b) + + def test_count(self): + result = self.h.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_find_album(self): + result = self.h.handle_request(u'find "album" "what"') + self.assert_(u'OK' in result) + + def test_find_album_without_quotes(self): + result = self.h.handle_request(u'find album "what"') + self.assert_(u'OK' in result) + + def test_find_artist(self): + result = self.h.handle_request(u'find "artist" "what"') + self.assert_(u'OK' in result) + + def test_find_artist_without_quotes(self): + result = self.h.handle_request(u'find artist "what"') + self.assert_(u'OK' in result) + + def test_find_title(self): + result = self.h.handle_request(u'find "title" "what"') + self.assert_(u'OK' in result) + + def test_find_title_without_quotes(self): + result = self.h.handle_request(u'find title "what"') + self.assert_(u'OK' in result) + + def test_find_else_should_fail(self): + result = self.h.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.h.handle_request(u'find album "album_what" artist "artist_what"') + self.assert_(u'OK' in result) + + def test_findadd(self): + result = self.h.handle_request(u'findadd "album" "what"') + self.assert_(u'OK' in result) + + def test_list_artist(self): + result = self.h.handle_request(u'list "artist"') + self.assert_(u'OK' in result) + + def test_list_artist_without_quotes(self): + result = self.h.handle_request(u'list artist') + self.assert_(u'OK' in result) + + def test_list_artist_without_quotes_and_capitalized(self): + result = self.h.handle_request(u'list Artist') + self.assert_(u'OK' in result) + + def test_list_artist_with_artist_should_fail(self): + result = self.h.handle_request(u'list "artist" "anartist"') + self.assertEqual(result[0], u'ACK [2@0] {list} incorrect arguments') + + def test_list_album_without_artist(self): + result = self.h.handle_request(u'list "album"') + self.assert_(u'OK' in result) + + def test_list_album_with_artist(self): + result = self.h.handle_request(u'list "album" "anartist"') + self.assert_(u'OK' in result) + + def test_list_album_artist_with_artist_without_quotes(self): + result = self.h.handle_request(u'list album artist "anartist"') + self.assert_(u'OK' in result) + + def test_listall(self): + result = self.h.handle_request(u'listall "file:///dev/urandom"') + self.assert_(u'ACK [0@0] {} Not implemented' in result) + + def test_listallinfo(self): + result = self.h.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.h.handle_request(u'lsinfo') + listplaylists_result = self.h.handle_request(u'listplaylists') + self.assertEqual(lsinfo_result, listplaylists_result) + + def test_lsinfo_with_empty_path_returns_same_as_listplaylists(self): + lsinfo_result = self.h.handle_request(u'lsinfo ""') + listplaylists_result = self.h.handle_request(u'listplaylists') + self.assertEqual(lsinfo_result, listplaylists_result) + + def test_lsinfo_for_root_returns_same_as_listplaylists(self): + lsinfo_result = self.h.handle_request(u'lsinfo "/"') + listplaylists_result = self.h.handle_request(u'listplaylists') + self.assertEqual(lsinfo_result, listplaylists_result) + + def test_search_album(self): + result = self.h.handle_request(u'search "album" "analbum"') + self.assert_(u'OK' in result) + + def test_search_album_without_quotes(self): + result = self.h.handle_request(u'search album "analbum"') + self.assert_(u'OK' in result) + + def test_search_artist(self): + result = self.h.handle_request(u'search "artist" "anartist"') + self.assert_(u'OK' in result) + + def test_search_artist_without_quotes(self): + result = self.h.handle_request(u'search artist "anartist"') + self.assert_(u'OK' in result) + + def test_search_filename(self): + result = self.h.handle_request(u'search "filename" "afilename"') + self.assert_(u'OK' in result) + + def test_search_filename_without_quotes(self): + result = self.h.handle_request(u'search filename "afilename"') + self.assert_(u'OK' in result) + + def test_search_title(self): + result = self.h.handle_request(u'search "title" "atitle"') + self.assert_(u'OK' in result) + + def test_search_title_without_quotes(self): + result = self.h.handle_request(u'search title "atitle"') + self.assert_(u'OK' in result) + + def test_search_any(self): + result = self.h.handle_request(u'search "any" "anything"') + self.assert_(u'OK' in result) + + def test_search_any_without_quotes(self): + result = self.h.handle_request(u'search any "anything"') + self.assert_(u'OK' in result) + + def test_search_else_should_fail(self): + result = self.h.handle_request(u'search "sometype" "something"') + self.assertEqual(result[0], u'ACK [2@0] {search} incorrect arguments') + + def test_update_without_uri(self): + result = self.h.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.h.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.h.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.h.handle_request(u'rescan "file:///dev/urandom"') + self.assert_(u'OK' in result) + self.assert_(u'updating_db: 0' in result) diff --git a/tests/mpd/playback_test.py b/tests/mpd/playback_test.py new file mode 100644 index 00000000..b3806090 --- /dev/null +++ b/tests/mpd/playback_test.py @@ -0,0 +1,217 @@ +import unittest + +from mopidy.backends.dummy import DummyBackend +from mopidy.mixers.dummy import DummyMixer +from mopidy.models import Track +from mopidy.mpd import frontend + +class PlaybackOptionsHandlerTest(unittest.TestCase): + def setUp(self): + self.m = DummyMixer() + self.b = DummyBackend(mixer=self.m) + self.h = frontend.MpdFrontend(backend=self.b) + + def test_consume_off(self): + result = self.h.handle_request(u'consume "0"') + self.assertFalse(self.b.playback.consume) + self.assert_(u'OK' in result) + + def test_consume_on(self): + result = self.h.handle_request(u'consume "1"') + self.assertTrue(self.b.playback.consume) + self.assert_(u'OK' in result) + + def test_crossfade(self): + result = self.h.handle_request(u'crossfade "10"') + self.assert_(u'ACK [0@0] {} Not implemented' in result) + + def test_random_off(self): + result = self.h.handle_request(u'random "0"') + self.assertFalse(self.b.playback.random) + self.assert_(u'OK' in result) + + def test_random_on(self): + result = self.h.handle_request(u'random "1"') + self.assertTrue(self.b.playback.random) + self.assert_(u'OK' in result) + + def test_repeat_off(self): + result = self.h.handle_request(u'repeat "0"') + self.assertFalse(self.b.playback.repeat) + self.assert_(u'OK' in result) + + def test_repeat_on(self): + result = self.h.handle_request(u'repeat "1"') + self.assertTrue(self.b.playback.repeat) + self.assert_(u'OK' in result) + + def test_setvol_below_min(self): + result = self.h.handle_request(u'setvol "-10"') + self.assert_(u'OK' in result) + self.assertEqual(0, self.b.mixer.volume) + + def test_setvol_min(self): + result = self.h.handle_request(u'setvol "0"') + self.assert_(u'OK' in result) + self.assertEqual(0, self.b.mixer.volume) + + def test_setvol_middle(self): + result = self.h.handle_request(u'setvol "50"') + self.assert_(u'OK' in result) + self.assertEqual(50, self.b.mixer.volume) + + def test_setvol_max(self): + result = self.h.handle_request(u'setvol "100"') + self.assert_(u'OK' in result) + self.assertEqual(100, self.b.mixer.volume) + + def test_setvol_above_max(self): + result = self.h.handle_request(u'setvol "110"') + self.assert_(u'OK' in result) + self.assertEqual(100, self.b.mixer.volume) + + def test_setvol_plus_is_ignored(self): + result = self.h.handle_request(u'setvol "+10"') + self.assert_(u'OK' in result) + self.assertEqual(10, self.b.mixer.volume) + + def test_single_off(self): + result = self.h.handle_request(u'single "0"') + self.assertFalse(self.b.playback.single) + self.assert_(u'OK' in result) + + def test_single_on(self): + result = self.h.handle_request(u'single "1"') + self.assertTrue(self.b.playback.single) + self.assert_(u'OK' in result) + + def test_replay_gain_mode_off(self): + result = self.h.handle_request(u'replay_gain_mode "off"') + self.assert_(u'ACK [0@0] {} Not implemented' in result) + + def test_replay_gain_mode_track(self): + result = self.h.handle_request(u'replay_gain_mode "track"') + self.assert_(u'ACK [0@0] {} Not implemented' in result) + + def test_replay_gain_mode_album(self): + result = self.h.handle_request(u'replay_gain_mode "album"') + self.assert_(u'ACK [0@0] {} Not implemented' in result) + + def test_replay_gain_status_default(self): + expected = u'off' + result = self.h.handle_request(u'replay_gain_status') + self.assert_(u'OK' in result) + self.assert_(expected in result) + + #def test_replay_gain_status_off(self): + # expected = u'off' + # self.h._replay_gain_mode(expected) + # result = self.h.handle_request(u'replay_gain_status') + # self.assert_(u'OK' in result) + # self.assert_(expected in result) + + #def test_replay_gain_status_track(self): + # expected = u'track' + # self.h._replay_gain_mode(expected) + # result = self.h.handle_request(u'replay_gain_status') + # self.assert_(u'OK' in result) + # self.assert_(expected in result) + + #def test_replay_gain_status_album(self): + # expected = u'album' + # self.h._replay_gain_mode(expected) + # result = self.h.handle_request(u'replay_gain_status') + # self.assert_(u'OK' in result) + # self.assert_(expected in result) + + +class PlaybackControlHandlerTest(unittest.TestCase): + def setUp(self): + self.m = DummyMixer() + self.b = DummyBackend(mixer=self.m) + self.h = frontend.MpdFrontend(backend=self.b) + + def test_next(self): + result = self.h.handle_request(u'next') + self.assert_(u'OK' in result) + + def test_pause_off(self): + track = Track() + self.b.current_playlist.load([track]) + self.h.handle_request(u'play "0"') + self.h.handle_request(u'pause "1"') + result = self.h.handle_request(u'pause "0"') + self.assert_(u'OK' in result) + self.assertEqual(self.b.playback.PLAYING, self.b.playback.state) + + def test_pause_on(self): + track = Track() + self.b.current_playlist.load([track]) + self.h.handle_request(u'play "0"') + result = self.h.handle_request(u'pause "1"') + self.assert_(u'OK' in result) + self.assertEqual(self.b.playback.PAUSED, self.b.playback.state) + + def test_play_without_pos(self): + track = Track() + self.b.current_playlist.load([track]) + self.b.playback.state = self.b.playback.PAUSED + result = self.h.handle_request(u'play') + self.assert_(u'OK' in result) + self.assertEqual(self.b.playback.PLAYING, self.b.playback.state) + + def test_play_with_pos(self): + self.b.current_playlist.load([Track()]) + result = self.h.handle_request(u'play "0"') + self.assert_(u'OK' in result) + self.assertEqual(self.b.playback.PLAYING, self.b.playback.state) + + def test_play_with_pos_out_of_bounds(self): + self.b.current_playlist.load([]) + result = self.h.handle_request(u'play "0"') + self.assertEqual(result[0], u'ACK [2@0] {play} Bad song index') + self.assertEqual(self.b.playback.STOPPED, self.b.playback.state) + + def test_play_minus_one_plays_first_in_playlist(self): + track = Track() + self.b.current_playlist.load([track]) + result = self.h.handle_request(u'play "-1"') + self.assert_(u'OK' in result) + self.assertEqual(self.b.playback.PLAYING, self.b.playback.state) + self.assertEqual(self.b.playback.current_track, track) + + def test_playid(self): + self.b.current_playlist.load([Track()]) + result = self.h.handle_request(u'playid "1"') + self.assert_(u'OK' in result) + self.assertEqual(self.b.playback.PLAYING, self.b.playback.state) + + def test_playid_minus_one_plays_first_in_playlist(self): + track = Track() + self.b.current_playlist.load([track]) + result = self.h.handle_request(u'playid "-1"') + self.assert_(u'OK' in result) + self.assertEqual(self.b.playback.PLAYING, self.b.playback.state) + self.assertEqual(self.b.playback.current_track, track) + + def test_playid_which_does_not_exist(self): + self.b.current_playlist.load([Track()]) + result = self.h.handle_request(u'playid "12345"') + self.assertEqual(result[0], u'ACK [50@0] {playid} No such song') + + def test_previous(self): + result = self.h.handle_request(u'previous') + self.assert_(u'OK' in result) + + def test_seek(self): + result = self.h.handle_request(u'seek "0" "30"') + self.assert_(u'ACK [0@0] {} Not implemented' in result) + + def test_seekid(self): + result = self.h.handle_request(u'seekid "0" "30"') + self.assert_(u'ACK [0@0] {} Not implemented' in result) + + def test_stop(self): + result = self.h.handle_request(u'stop') + self.assert_(u'OK' in result) + self.assertEqual(self.b.playback.STOPPED, self.b.playback.state) diff --git a/tests/mpd/reflection_test.py b/tests/mpd/reflection_test.py new file mode 100644 index 00000000..70af604f --- /dev/null +++ b/tests/mpd/reflection_test.py @@ -0,0 +1,44 @@ +import unittest + +from mopidy.backends.dummy import DummyBackend +from mopidy.mixers.dummy import DummyMixer +from mopidy.mpd import frontend + +class ReflectionHandlerTest(unittest.TestCase): + def setUp(self): + self.m = DummyMixer() + self.b = DummyBackend(mixer=self.m) + self.h = frontend.MpdFrontend(backend=self.b) + + def test_commands_returns_list_of_all_commands(self): + result = self.h.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 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_decoders(self): + result = self.h.handle_request(u'decoders') + self.assert_(u'ACK [0@0] {} Not implemented' in result) + + def test_notcommands_returns_only_ok(self): + result = self.h.handle_request(u'notcommands') + self.assertEqual(1, len(result)) + self.assert_(u'OK' in result) + + def test_tagtypes(self): + result = self.h.handle_request(u'tagtypes') + self.assert_(u'OK' in result) + + def test_urlhandlers(self): + result = self.h.handle_request(u'urlhandlers') + self.assert_(u'OK' in result) + self.assert_(u'handler: dummy:' in result) diff --git a/tests/mpd/request_handler_test.py b/tests/mpd/request_handler_test.py new file mode 100644 index 00000000..1dcba9ad --- /dev/null +++ b/tests/mpd/request_handler_test.py @@ -0,0 +1,48 @@ +import unittest + +from mopidy.backends.dummy import DummyBackend +from mopidy.mixers.dummy import DummyMixer +from mopidy.mpd import frontend, MpdAckError + +class RequestHandlerTest(unittest.TestCase): + def setUp(self): + self.m = DummyMixer() + self.b = DummyBackend(mixer=self.m) + self.h = frontend.MpdFrontend(backend=self.b) + + def test_register_same_pattern_twice_fails(self): + func = lambda: None + try: + frontend.handle_pattern('a pattern')(func) + frontend.handle_pattern('a pattern')(func) + self.fail('Registering a pattern twice shoulde raise ValueError') + except ValueError: + pass + + def test_finding_handler_for_unknown_command_raises_exception(self): + try: + self.h.find_handler('an_unknown_command with args') + self.fail('Should raise exception') + except MpdAckError as e: + self.assertEqual(e.get_mpd_ack(), + u'ACK [5@0] {} unknown command "an_unknown_command"') + + def test_finding_handler_for_known_command_returns_handler_and_kwargs(self): + expected_handler = lambda x: None + frontend._request_handlers['known_command (?P.+)'] = \ + expected_handler + (handler, kwargs) = self.h.find_handler('known_command an_arg') + self.assertEqual(handler, expected_handler) + self.assert_('arg1' in kwargs) + self.assertEqual(kwargs['arg1'], 'an_arg') + + def test_handling_unknown_request_yields_error(self): + result = self.h.handle_request('an unhandled request') + self.assertEqual(result[0], u'ACK [5@0] {} unknown command "an"') + + def test_handling_known_request(self): + expected = 'magic' + frontend._request_handlers['known request'] = lambda x: expected + result = self.h.handle_request('known request') + self.assert_(u'OK' in result) + self.assert_(expected in result) diff --git a/tests/mpd/status_test.py b/tests/mpd/status_test.py new file mode 100644 index 00000000..ae1ac816 --- /dev/null +++ b/tests/mpd/status_test.py @@ -0,0 +1,204 @@ +import unittest + +from mopidy.backends.dummy import DummyBackend +from mopidy.mixers.dummy import DummyMixer +from mopidy.models import Track +from mopidy.mpd import frontend + +class StatusHandlerTest(unittest.TestCase): + def setUp(self): + self.m = DummyMixer() + self.b = DummyBackend(mixer=self.m) + self.h = frontend.MpdFrontend(backend=self.b) + + def test_clearerror(self): + result = self.h.handle_request(u'clearerror') + self.assert_(u'ACK [0@0] {} Not implemented' in result) + + def test_currentsong(self): + track = Track() + self.b.current_playlist.load([track]) + self.b.playback.play() + result = self.h.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: 1' in result) + self.assert_(u'OK' in result) + + def test_currentsong_without_song(self): + result = self.h.handle_request(u'currentsong') + self.assert_(u'OK' in result) + + def test_idle_without_subsystems(self): + result = self.h.handle_request(u'idle') + self.assert_(u'OK' in result) + + def test_idle_with_subsystems(self): + result = self.h.handle_request(u'idle database playlist') + self.assert_(u'OK' in result) + + def test_noidle(self): + result = self.h.handle_request(u'noidle') + self.assert_(u'OK' in result) + + def test_stats_command(self): + result = self.h.handle_request(u'stats') + self.assert_(u'OK' in result) + + def test_stats_method(self): + result = self.h._status_stats() + self.assert_('artists' in result) + self.assert_(int(result['artists']) >= 0) + self.assert_('albums' in result) + self.assert_(int(result['albums']) >= 0) + self.assert_('songs' in result) + self.assert_(int(result['songs']) >= 0) + self.assert_('uptime' in result) + self.assert_(int(result['uptime']) >= 0) + self.assert_('db_playtime' in result) + self.assert_(int(result['db_playtime']) >= 0) + self.assert_('db_update' in result) + self.assert_(int(result['db_update']) >= 0) + self.assert_('playtime' in result) + self.assert_(int(result['playtime']) >= 0) + + def test_status_command(self): + result = self.h.handle_request(u'status') + self.assert_(u'OK' in result) + + def test_status_method_contains_volume_which_defaults_to_0(self): + result = dict(self.h._status_status()) + self.assert_('volume' in result) + self.assertEqual(int(result['volume']), 0) + + def test_status_method_contains_volume(self): + self.b.mixer.volume = 17 + result = dict(self.h._status_status()) + self.assert_('volume' in result) + self.assertEqual(int(result['volume']), 17) + + def test_status_method_contains_repeat_is_0(self): + result = dict(self.h._status_status()) + self.assert_('repeat' in result) + self.assertEqual(int(result['repeat']), 0) + + def test_status_method_contains_repeat_is_1(self): + self.b.playback.repeat = 1 + result = dict(self.h._status_status()) + self.assert_('repeat' in result) + self.assertEqual(int(result['repeat']), 1) + + def test_status_method_contains_random_is_0(self): + result = dict(self.h._status_status()) + self.assert_('random' in result) + self.assertEqual(int(result['random']), 0) + + def test_status_method_contains_random_is_1(self): + self.b.playback.random = 1 + result = dict(self.h._status_status()) + self.assert_('random' in result) + self.assertEqual(int(result['random']), 1) + + def test_status_method_contains_single(self): + result = dict(self.h._status_status()) + self.assert_('single' in result) + self.assert_(int(result['single']) in (0, 1)) + + def test_status_method_contains_consume_is_0(self): + result = dict(self.h._status_status()) + self.assert_('consume' in result) + self.assertEqual(int(result['consume']), 0) + + def test_status_method_contains_consume_is_1(self): + self.b.playback.consume = 1 + result = dict(self.h._status_status()) + self.assert_('consume' in result) + self.assertEqual(int(result['consume']), 1) + + def test_status_method_contains_playlist(self): + result = dict(self.h._status_status()) + self.assert_('playlist' in result) + self.assert_(int(result['playlist']) in xrange(0, 2**31 - 1)) + + def test_status_method_contains_playlistlength(self): + result = dict(self.h._status_status()) + self.assert_('playlistlength' in result) + self.assert_(int(result['playlistlength']) >= 0) + + def test_status_method_contains_xfade(self): + result = dict(self.h._status_status()) + self.assert_('xfade' in result) + self.assert_(int(result['xfade']) >= 0) + + def test_status_method_contains_state_is_play(self): + self.b.playback.state = self.b.playback.PLAYING + result = dict(self.h._status_status()) + self.assert_('state' in result) + self.assertEqual(result['state'], 'play') + + def test_status_method_contains_state_is_stop(self): + self.b.playback.state = self.b.playback.STOPPED + result = dict(self.h._status_status()) + self.assert_('state' in result) + self.assertEqual(result['state'], 'stop') + + def test_status_method_contains_state_is_pause(self): + self.b.playback.state = self.b.playback.PLAYING + self.b.playback.state = self.b.playback.PAUSED + result = dict(self.h._status_status()) + self.assert_('state' in result) + self.assertEqual(result['state'], 'pause') + + def test_status_method_when_playlist_loaded_contains_song(self): + self.b.current_playlist.load([Track()]) + self.b.playback.play() + result = dict(self.h._status_status()) + self.assert_('song' in result) + self.assert_(int(result['song']) >= 0) + + def test_status_method_when_playlist_loaded_contains_cpid_as_songid(self): + self.b.current_playlist.load([Track()]) + self.b.playback.play() + result = dict(self.h._status_status()) + self.assert_('songid' in result) + self.assertEqual(int(result['songid']), 1) + + def test_status_method_when_playing_contains_time_with_no_length(self): + self.b.current_playlist.load([Track(length=None)]) + self.b.playback.play() + result = dict(self.h._status_status()) + self.assert_('time' in result) + (position, total) = result['time'].split(':') + position = int(position) + total = int(total) + self.assert_(position <= total) + + def test_status_method_when_playing_contains_time_with_length(self): + self.b.current_playlist.load([Track(length=10000)]) + self.b.playback.play() + result = dict(self.h._status_status()) + self.assert_('time' in result) + (position, total) = result['time'].split(':') + position = int(position) + total = int(total) + self.assert_(position <= total) + + def test_status_method_when_playing_contains_elapsed(self): + self.b.playback.state = self.b.playback.PAUSED + self.b.playback._play_time_accumulated = 59123 + result = dict(self.h._status_status()) + self.assert_('elapsed' in result) + self.assertEqual(int(result['elapsed']), 59123) + + def test_status_method_when_playing_contains_bitrate(self): + self.b.current_playlist.load([Track(bitrate=320)]) + self.b.playback.play() + result = dict(self.h._status_status()) + self.assert_('bitrate' in result) + self.assertEqual(int(result['bitrate']), 320) diff --git a/tests/mpd/stickers_test.py b/tests/mpd/stickers_test.py new file mode 100644 index 00000000..437251a4 --- /dev/null +++ b/tests/mpd/stickers_test.py @@ -0,0 +1,41 @@ +import unittest + +from mopidy.backends.dummy import DummyBackend +from mopidy.mixers.dummy import DummyMixer +from mopidy.mpd import frontend + +class StickersHandlerTest(unittest.TestCase): + def setUp(self): + self.m = DummyMixer() + self.b = DummyBackend(mixer=self.m) + self.h = frontend.MpdFrontend(backend=self.b) + + def test_sticker_get(self): + result = self.h.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.h.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.h.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.h.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.h.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.h.handle_request( + u'sticker find "song" "file:///dev/urandom" "a_name"') + self.assert_(u'ACK [0@0] {} Not implemented' in result) diff --git a/tests/mpd/stored_playlists_test.py b/tests/mpd/stored_playlists_test.py new file mode 100644 index 00000000..b616695e --- /dev/null +++ b/tests/mpd/stored_playlists_test.py @@ -0,0 +1,87 @@ +import datetime as dt +import unittest + +from mopidy.backends.dummy import DummyBackend +from mopidy.mixers.dummy import DummyMixer +from mopidy.models import Track, Playlist +from mopidy.mpd import frontend + +from tests import SkipTest + +class StoredPlaylistsHandlerTest(unittest.TestCase): + def setUp(self): + self.m = DummyMixer() + self.b = DummyBackend(mixer=self.m) + self.h = frontend.MpdFrontend(backend=self.b) + + def test_listplaylist(self): + self.b.stored_playlists.playlists = [ + Playlist(name='name', tracks=[Track(uri='file:///dev/urandom')])] + result = self.h.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.h.handle_request(u'listplaylist "name"') + self.assertEqual(result[0], + u'ACK [50@0] {listplaylist} No such playlist') + + def test_listplaylistinfo(self): + self.b.stored_playlists.playlists = [ + Playlist(name='name', tracks=[Track(uri='file:///dev/urandom')])] + result = self.h.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.h.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.b.stored_playlists.playlists = [Playlist(name='a', + last_modified=last_modified)] + result = self.h.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(self): + result = self.h.handle_request(u'load "name"') + self.assert_(u'OK' in result) + + def test_load_appends(self): + raise SkipTest + + def test_playlistadd(self): + result = self.h.handle_request( + u'playlistadd "name" "file:///dev/urandom"') + self.assert_(u'ACK [0@0] {} Not implemented' in result) + + def test_playlistclear(self): + result = self.h.handle_request(u'playlistclear "name"') + self.assert_(u'ACK [0@0] {} Not implemented' in result) + + def test_playlistdelete(self): + result = self.h.handle_request(u'playlistdelete "name" "5"') + self.assert_(u'ACK [0@0] {} Not implemented' in result) + + def test_playlistmove(self): + result = self.h.handle_request(u'playlistmove "name" "5" "10"') + self.assert_(u'ACK [0@0] {} Not implemented' in result) + + def test_rename(self): + result = self.h.handle_request(u'rename "old_name" "new_name"') + self.assert_(u'ACK [0@0] {} Not implemented' in result) + + def test_rm(self): + result = self.h.handle_request(u'rm "name"') + self.assert_(u'ACK [0@0] {} Not implemented' in result) + + def test_save(self): + result = self.h.handle_request(u'save "name"') + self.assert_(u'ACK [0@0] {} Not implemented' in result) From b4c80fdc2ffb9afce9cfafd8a5f4d735125456ae Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Sat, 31 Jul 2010 20:21:36 +0200 Subject: [PATCH 09/11] tests: Remove unused imports --- tests/mixers/{dummy_test.py => base_test.py} | 0 tests/mixers/denon_test.py | 4 +--- tests/utils_test.py | 1 - 3 files changed, 1 insertion(+), 4 deletions(-) rename tests/mixers/{dummy_test.py => base_test.py} (100%) diff --git a/tests/mixers/dummy_test.py b/tests/mixers/base_test.py similarity index 100% rename from tests/mixers/dummy_test.py rename to tests/mixers/base_test.py diff --git a/tests/mixers/denon_test.py b/tests/mixers/denon_test.py index 82e813e9..bf387418 100644 --- a/tests/mixers/denon_test.py +++ b/tests/mixers/denon_test.py @@ -1,7 +1,5 @@ -import unittest - from mopidy.mixers.denon import DenonMixer -from tests.mixers.dummy_test import BaseMixerTest +from tests.mixers.base_test import BaseMixerTest class DenonMixerDeviceMock(object): def __init__(self): diff --git a/tests/utils_test.py b/tests/utils_test.py index 59f77ec7..eacd314e 100644 --- a/tests/utils_test.py +++ b/tests/utils_test.py @@ -5,7 +5,6 @@ import sys import shutil import tempfile import unittest -import urllib from mopidy.utils import * from mopidy.models import Track, Artist, Album From c3a228fbfd567be068913e107349c46ffc63c745 Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Sat, 31 Jul 2010 23:13:45 +0200 Subject: [PATCH 10/11] Despotify: Catch and log SpytifyError --- docs/changes.rst | 4 ++++ mopidy/backends/despotify.py | 35 ++++++++++++++++++++++++++--------- 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/docs/changes.rst b/docs/changes.rst index 7b312dd8..9c426795 100644 --- a/docs/changes.rst +++ b/docs/changes.rst @@ -31,6 +31,10 @@ We got an updated :doc:`release roadmap `! - Having multiple identical tracks in a playlist is now working properly. (CPID refactoring) +- Despotify backend: + + - Catch and log :exc:`spytify.SpytifyError`. (Fixes: :issue:`11`) + - Libspotify backend: - Fix choppy playback using the Libspotify backend by using blocking ALSA diff --git a/mopidy/backends/despotify.py b/mopidy/backends/despotify.py index 49809620..8779f40b 100644 --- a/mopidy/backends/despotify.py +++ b/mopidy/backends/despotify.py @@ -76,20 +76,36 @@ class DespotifyLibraryController(BaseLibraryController): class DespotifyPlaybackController(BasePlaybackController): def _pause(self): - self.backend.spotify.pause() - return True + try: + self.backend.spotify.pause() + return True + except spytify.SpytifyError as e: + logger.error(e) + return False def _play(self, track): - self.backend.spotify.play(self.backend.spotify.lookup(track.uri)) - return True + try: + self.backend.spotify.play(self.backend.spotify.lookup(track.uri)) + return True + except spytify.SpytifyError as e: + logger.error(e) + return False def _resume(self): - self.backend.spotify.resume() - return True + try: + self.backend.spotify.resume() + return True + except spytify.SpytifyError as e: + logger.error(e) + return False def _stop(self): - self.backend.spotify.stop() - return True + try: + self.backend.spotify.stop() + return True + except spytify.SpytifyError as e: + logger.error(e) + return False class DespotifyStoredPlaylistsController(BaseStoredPlaylistsController): @@ -146,7 +162,8 @@ class DespotifyTranslator(object): return Playlist( uri=spotify_playlist.get_uri(), name=spotify_playlist.name.decode(ENCODING), - tracks=filter(None, [cls.to_mopidy_track(t) for t in spotify_playlist.tracks]), + tracks=filter(None, + [cls.to_mopidy_track(t) for t in spotify_playlist.tracks]), ) From 6d5d4d857d8bf6500e3eb64a72593b8db51be6d7 Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Sat, 31 Jul 2010 23:25:25 +0200 Subject: [PATCH 11/11] Make test match test name --- tests/backends/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/backends/base.py b/tests/backends/base.py index 4c1ed0ee..a7d17a94 100644 --- a/tests/backends/base.py +++ b/tests/backends/base.py @@ -565,7 +565,7 @@ class BasePlaybackControllerTest(object): @populate_playlist def test_previous_track_with_random(self): - self.playback.repeat = True + self.playback.random = True for track in self.tracks: self.playback.next() self.assertEqual(self.playback.previous_track,