mpd: Final cleanup of PR #1187, #1308 and #1322

Fixes #1014, fixes #1322
This commit is contained in:
Stein Magnus Jodal 2015-10-29 15:01:05 +01:00
parent 7aa8aa2967
commit 8aeb9841c5
8 changed files with 117 additions and 65 deletions

View File

@ -72,3 +72,6 @@
- Cadel Watson <cadel@cadelwatson.com> - Cadel Watson <cadel@cadelwatson.com>
- Loïck Bonniot <git@lesterpig.com> - Loïck Bonniot <git@lesterpig.com>
- Gustaf Hallberg <ghallberg@gmail.com> - Gustaf Hallberg <ghallberg@gmail.com>
- kozec <kozec@kozec.com>
- Jelle van der Waa <jelle@vdwaa.nl>
- Alex Malone <jalexmalone@gmail.com>

View File

@ -25,6 +25,18 @@ Local backend
MPD frontend MPD frontend
------------ ------------
- Implemented commands for modifying stored playlists:
- ``playlistadd``
- ``playlistclear``
- ``playlistdelete``
- ``playlistmove``
- ``rename``
- ``rm``
- ``save``
(Fixes: :issue:`1014`, PR: :issue:`1187`, :issue:`1308`, :issue:`1322`)
- Start ``songid`` counting at 1 instead of 0 to match the original MPD server. - Start ``songid`` counting at 1 instead of 0 to match the original MPD server.
Zeroconf Zeroconf
@ -236,9 +248,6 @@ Core API
:issue:`997` PR: :issue:`1225`) :issue:`997` PR: :issue:`1225`)
- Added ``playlist_deleted`` event. (Fixes: :issue:`996`) - Added ``playlist_deleted`` event. (Fixes: :issue:`996`)
=======
- MPD: Implemented commands for modifying stored playlists (PR: :issue:`1187`)
Models Models
------ ------

View File

@ -45,7 +45,6 @@ Items on this list will probably not be supported in the near future.
The following items are currently not supported, but should be added in the The following items are currently not supported, but should be added in the
near future: near future:
- Modifying stored playlists is not supported
- ``tagtypes`` is not supported - ``tagtypes`` is not supported
- Live update of the music database is not supported - Live update of the music database is not supported

View File

@ -25,7 +25,7 @@ class Extension(ext.Extension):
schema['connection_timeout'] = config.Integer(minimum=1) schema['connection_timeout'] = config.Integer(minimum=1)
schema['zeroconf'] = config.String(optional=True) schema['zeroconf'] = config.String(optional=True)
schema['command_blacklist'] = config.List(optional=True) schema['command_blacklist'] = config.List(optional=True)
schema['default_playlist_scheme'] = config.String(optional=False) schema['default_playlist_scheme'] = config.String()
return schema return schema
def validate_environment(self): def validate_environment(self):

View File

@ -70,6 +70,9 @@ class MpdFrontend(pykka.ThreadingActor, CoreListener):
def playlist_changed(self, playlist): def playlist_changed(self, playlist):
self.send_idle('stored_playlist') self.send_idle('stored_playlist')
def playlist_deleted(self, playlist):
self.send_idle('stored_playlist')
def options_changed(self): def options_changed(self):
self.send_idle('options') self.send_idle('options')

View File

@ -93,24 +93,24 @@ class MpdNotImplemented(MpdAckError):
class MpdInvalidTrackForPlaylist(MpdAckError): class MpdInvalidTrackForPlaylist(MpdAckError):
# NOTE: This is a custom error for Mopidy that does not exist in MPD.
error_code = 0 error_code = 0
def __init__(self, backend_scheme, track_scheme, *args, **kwargs): def __init__(self, playlist_scheme, track_scheme, *args, **kwargs):
super(MpdInvalidTrackForPlaylist, self).__init__(*args, **kwargs) super(MpdInvalidTrackForPlaylist, self).__init__(*args, **kwargs)
self.message = 'Playlist backend "%s" can\'t store ' \ self.message = (
'track scheme "%s"' % (backend_scheme, track_scheme) 'Playlist with scheme "%s" can\'t store track scheme "%s"' %
(playlist_scheme, track_scheme))
class MpdFailedToSavePlaylist(MpdAckError): class MpdFailedToSavePlaylist(MpdAckError):
# NOTE: This is a custom error for Mopidy that does not exist in MPD.
error_code = 0 error_code = 0
def __init__(self, backend_scheme, *args, **kwargs): def __init__(self, backend_scheme, *args, **kwargs):
super(MpdFailedToSavePlaylist, self).__init__(*args, **kwargs) super(MpdFailedToSavePlaylist, self).__init__(*args, **kwargs)
if backend_scheme is None: self.message = 'Backend with scheme "%s" failed to save playlist' % (
self.message = 'Failed to save playlist' backend_scheme)
else:
self.message = 'Backend "%s" failed to save playlist' % (
backend_scheme, )
class MpdDisabled(MpdAckError): class MpdDisabled(MpdAckError):

View File

@ -150,22 +150,28 @@ def playlistadd(context, name, track_uri):
``NAME.m3u`` will be created if it does not exist. ``NAME.m3u`` will be created if it does not exist.
""" """
uri = context.lookup_playlist_uri_from_name(name) uri = context.lookup_playlist_uri_from_name(name)
playlist = uri is not None and context.core.playlists.lookup(uri).get() old_playlist = uri is not None and context.core.playlists.lookup(uri).get()
if not playlist: if not old_playlist:
# Create new playlist with this single track # Create new playlist with this single track
lookup_res = context.core.library.lookup(uris=[track_uri]).get() lookup_res = context.core.library.lookup(uris=[track_uri]).get()
tracks = [track for selections in lookup_res.values() tracks = [
for track in selections] track
for uri_tracks in lookup_res.values()
for track in uri_tracks]
_create_playlist(context, name, tracks) _create_playlist(context, name, tracks)
else: else:
# Add track to existing playlist # Add track to existing playlist
uri_scheme = urlparse.urlparse(track_uri).scheme
lookup_res = context.core.library.lookup(uris=[track_uri]).get() lookup_res = context.core.library.lookup(uris=[track_uri]).get()
to_add = [track for selections in lookup_res.values() new_tracks = [
for track in selections] track
playlist = playlist.replace(tracks=list(playlist.tracks) + to_add) for uri_tracks in lookup_res.values()
if context.core.playlists.save(playlist).get() is None: for track in uri_tracks]
playlist_scheme = urlparse.urlparse(playlist.uri).scheme new_playlist = old_playlist.replace(
tracks=list(old_playlist.tracks) + new_tracks)
saved_playlist = context.core.playlists.save(new_playlist).get()
if saved_playlist is None:
playlist_scheme = urlparse.urlparse(old_playlist.uri).scheme
uri_scheme = urlparse.urlparse(track_uri).scheme
raise exceptions.MpdInvalidTrackForPlaylist( raise exceptions.MpdInvalidTrackForPlaylist(
playlist_scheme, uri_scheme) playlist_scheme, uri_scheme)
@ -176,28 +182,29 @@ def _create_playlist(context, name, tracks):
""" """
uri_schemes = set([urlparse.urlparse(t.uri).scheme for t in tracks]) uri_schemes = set([urlparse.urlparse(t.uri).scheme for t in tracks])
for scheme in uri_schemes: for scheme in uri_schemes:
playlist = context.core.playlists.create(name, scheme).get() new_playlist = context.core.playlists.create(name, scheme).get()
if not playlist: if new_playlist is None:
# Backend can't create playlists at all logger.debug(
logger.warning("%s backend can't create playlists", scheme) "Backend for scheme %s can't create playlists", scheme)
continue continue # Backend can't create playlists at all
playlist = playlist.replace(tracks=tracks) new_playlist = new_playlist.replace(tracks=tracks)
if context.core.playlists.save(playlist).get() is None: saved_playlist = context.core.playlists.save(new_playlist).get()
# Failed to save using this backend if saved_playlist is not None:
continue return # Created and saved
# Created and saved else:
return continue # Failed to save using this backend
# Can't use backend appropriate for passed uri schemes, use default one # Can't use backend appropriate for passed URI schemes, use default one
default_scheme = context.dispatcher.config[ default_scheme = context.dispatcher.config[
'mpd']['default_playlist_scheme'] 'mpd']['default_playlist_scheme']
playlist = context.core.playlists.create(name, default_scheme).get() new_playlist = context.core.playlists.create(name, default_scheme).get()
if not playlist: if new_playlist is None:
# If even MPD's default backend can't save playlist, everything is lost # If even MPD's default backend can't save playlist, everything is lost
logger.warning("Default backend can't create playlists") logger.warning("MPD's default backend can't create playlists")
raise exceptions.MpdFailedToSavePlaylist(None) raise exceptions.MpdFailedToSavePlaylist(default_scheme)
playlist = playlist.replace(tracks=tracks) new_playlist = new_playlist.replace(tracks=tracks)
if context.core.playlists.save(playlist).get() is None: saved_playlist = context.core.playlists.save(new_playlist).get()
uri_scheme = urlparse.urlparse(playlist.uri).scheme if saved_playlist is None:
uri_scheme = urlparse.urlparse(new_playlist.uri).scheme
raise exceptions.MpdFailedToSavePlaylist(uri_scheme) raise exceptions.MpdFailedToSavePlaylist(uri_scheme)
@ -210,7 +217,7 @@ def playlistclear(context, name):
Clears the playlist ``NAME.m3u``. Clears the playlist ``NAME.m3u``.
``NAME.m3u`` will be created if it does not exist. The playlist will be created if it does not exist.
""" """
uri = context.lookup_playlist_uri_from_name(name) uri = context.lookup_playlist_uri_from_name(name)
playlist = uri is not None and context.core.playlists.lookup(uri).get() playlist = uri is not None and context.core.playlists.lookup(uri).get()
@ -243,7 +250,8 @@ def playlistdelete(context, name, songpos):
# Replace tracks and save playlist # Replace tracks and save playlist
playlist = playlist.replace(tracks=tracks) playlist = playlist.replace(tracks=tracks)
if context.core.playlists.save(playlist).get() is None: saved_playlist = context.core.playlists.save(playlist).get()
if saved_playlist is None:
raise exceptions.MpdFailedToSavePlaylist(urlparse.urlparse(uri).scheme) raise exceptions.MpdFailedToSavePlaylist(urlparse.urlparse(uri).scheme)
@ -278,7 +286,8 @@ def playlistmove(context, name, from_pos, to_pos):
# Replace tracks and save playlist # Replace tracks and save playlist
playlist = playlist.replace(tracks=tracks) playlist = playlist.replace(tracks=tracks)
if context.core.playlists.save(playlist).get() is None: saved_playlist = context.core.playlists.save(playlist).get()
if saved_playlist is None:
raise exceptions.MpdFailedToSavePlaylist(urlparse.urlparse(uri).scheme) raise exceptions.MpdFailedToSavePlaylist(urlparse.urlparse(uri).scheme)
@ -293,15 +302,17 @@ def rename(context, old_name, new_name):
""" """
uri = context.lookup_playlist_uri_from_name(old_name) uri = context.lookup_playlist_uri_from_name(old_name)
uri_scheme = urlparse.urlparse(uri).scheme uri_scheme = urlparse.urlparse(uri).scheme
playlist = uri is not None and context.core.playlists.lookup(uri).get() old_playlist = uri is not None and context.core.playlists.lookup(uri).get()
if not playlist: if not old_playlist:
raise exceptions.MpdNoExistError('No such playlist') raise exceptions.MpdNoExistError('No such playlist')
# Create copy of the playlist and remove original # Create copy of the playlist and remove original
copy = context.core.playlists.create(new_name, uri_scheme).get() new_playlist = context.core.playlists.create(new_name, uri_scheme).get()
copy = copy.replace(tracks=playlist.tracks) new_playlist = new_playlist.replace(tracks=old_playlist.tracks)
context.core.playlists.save(copy).get() saved_playlist = context.core.playlists.save(new_playlist).get()
context.core.playlists.delete(uri).get() if saved_playlist is None:
raise exceptions.MpdFailedToSavePlaylist(uri_scheme)
context.core.playlists.delete(old_playlist.uri).get()
@protocol.commands.add('rm') @protocol.commands.add('rm')
@ -335,7 +346,8 @@ def save(context, name):
_create_playlist(context, name, tracks) _create_playlist(context, name, tracks)
else: else:
# Overwrite existing playlist # Overwrite existing playlist
playlist = playlist.replace(tracks=tracks) new_playlist = playlist.replace(tracks=tracks)
if not context.core.playlists.save(playlist).get(): saved_playlist = context.core.playlists.save(new_playlist).get()
if saved_playlist is None:
raise exceptions.MpdFailedToSavePlaylist( raise exceptions.MpdFailedToSavePlaylist(
urlparse.urlparse(uri).scheme) urlparse.urlparse(uri).scheme)

View File

@ -16,6 +16,7 @@ class PlaylistsHandlerTest(protocol.BaseTestCase):
name='name', uri='dummy:name', tracks=[Track(uri='dummy:a')])]) name='name', uri='dummy:name', tracks=[Track(uri='dummy:a')])])
self.send_request('listplaylist "name"') self.send_request('listplaylist "name"')
self.assertInResponse('file: dummy:a') self.assertInResponse('file: dummy:a')
self.assertInResponse('OK') self.assertInResponse('OK')
@ -25,11 +26,13 @@ class PlaylistsHandlerTest(protocol.BaseTestCase):
name='name', uri='dummy:name', tracks=[Track(uri='dummy:a')])]) name='name', uri='dummy:name', tracks=[Track(uri='dummy:a')])])
self.send_request('listplaylist name') self.send_request('listplaylist name')
self.assertInResponse('file: dummy:a') self.assertInResponse('file: dummy:a')
self.assertInResponse('OK') self.assertInResponse('OK')
def test_listplaylist_fails_if_no_playlist_is_found(self): def test_listplaylist_fails_if_no_playlist_is_found(self):
self.send_request('listplaylist "name"') self.send_request('listplaylist "name"')
self.assertEqualResponse('ACK [50@0] {listplaylist} No such playlist') self.assertEqualResponse('ACK [50@0] {listplaylist} No such playlist')
def test_listplaylist_duplicate(self): def test_listplaylist_duplicate(self):
@ -38,6 +41,7 @@ class PlaylistsHandlerTest(protocol.BaseTestCase):
self.backend.playlists.set_dummy_playlists([playlist1, playlist2]) self.backend.playlists.set_dummy_playlists([playlist1, playlist2])
self.send_request('listplaylist "a [2]"') self.send_request('listplaylist "a [2]"')
self.assertInResponse('file: c') self.assertInResponse('file: c')
self.assertInResponse('OK') self.assertInResponse('OK')
@ -47,6 +51,7 @@ class PlaylistsHandlerTest(protocol.BaseTestCase):
name='name', uri='dummy:name', tracks=[Track(uri='dummy:a')])]) name='name', uri='dummy:name', tracks=[Track(uri='dummy:a')])])
self.send_request('listplaylistinfo "name"') self.send_request('listplaylistinfo "name"')
self.assertInResponse('file: dummy:a') self.assertInResponse('file: dummy:a')
self.assertNotInResponse('Track: 0') self.assertNotInResponse('Track: 0')
self.assertNotInResponse('Pos: 0') self.assertNotInResponse('Pos: 0')
@ -58,6 +63,7 @@ class PlaylistsHandlerTest(protocol.BaseTestCase):
name='name', uri='dummy:name', tracks=[Track(uri='dummy:a')])]) name='name', uri='dummy:name', tracks=[Track(uri='dummy:a')])])
self.send_request('listplaylistinfo name') self.send_request('listplaylistinfo name')
self.assertInResponse('file: dummy:a') self.assertInResponse('file: dummy:a')
self.assertNotInResponse('Track: 0') self.assertNotInResponse('Track: 0')
self.assertNotInResponse('Pos: 0') self.assertNotInResponse('Pos: 0')
@ -65,6 +71,7 @@ class PlaylistsHandlerTest(protocol.BaseTestCase):
def test_listplaylistinfo_fails_if_no_playlist_is_found(self): def test_listplaylistinfo_fails_if_no_playlist_is_found(self):
self.send_request('listplaylistinfo "name"') self.send_request('listplaylistinfo "name"')
self.assertEqualResponse( self.assertEqualResponse(
'ACK [50@0] {listplaylistinfo} No such playlist') 'ACK [50@0] {listplaylistinfo} No such playlist')
@ -74,6 +81,7 @@ class PlaylistsHandlerTest(protocol.BaseTestCase):
self.backend.playlists.set_dummy_playlists([playlist1, playlist2]) self.backend.playlists.set_dummy_playlists([playlist1, playlist2])
self.send_request('listplaylistinfo "a [2]"') self.send_request('listplaylistinfo "a [2]"')
self.assertInResponse('file: c') self.assertInResponse('file: c')
self.assertNotInResponse('Track: 0') self.assertNotInResponse('Track: 0')
self.assertNotInResponse('Pos: 0') self.assertNotInResponse('Pos: 0')
@ -86,6 +94,7 @@ class PlaylistsHandlerTest(protocol.BaseTestCase):
Playlist(name='a', uri='dummy:a')]) Playlist(name='a', uri='dummy:a')])
self.send_request('listplaylists') self.send_request('listplaylists')
self.assertInResponse('playlist: a') self.assertInResponse('playlist: a')
# Date without milliseconds and with time zone information # Date without milliseconds and with time zone information
self.assertInResponse('Last-Modified: 2015-08-05T22:51:06Z') self.assertInResponse('Last-Modified: 2015-08-05T22:51:06Z')
@ -97,6 +106,7 @@ class PlaylistsHandlerTest(protocol.BaseTestCase):
self.backend.playlists.set_dummy_playlists([playlist1, playlist2]) self.backend.playlists.set_dummy_playlists([playlist1, playlist2])
self.send_request('listplaylists') self.send_request('listplaylists')
self.assertInResponse('playlist: a') self.assertInResponse('playlist: a')
self.assertInResponse('playlist: a [2]') self.assertInResponse('playlist: a [2]')
self.assertInResponse('OK') self.assertInResponse('OK')
@ -107,13 +117,16 @@ class PlaylistsHandlerTest(protocol.BaseTestCase):
Playlist(name='', uri='dummy:', last_modified=last_modified)]) Playlist(name='', uri='dummy:', last_modified=last_modified)])
self.send_request('listplaylists') self.send_request('listplaylists')
self.assertNotInResponse('playlist: ') self.assertNotInResponse('playlist: ')
self.assertInResponse('OK') self.assertInResponse('OK')
def test_listplaylists_replaces_newline_with_space(self): def test_listplaylists_replaces_newline_with_space(self):
self.backend.playlists.set_dummy_playlists([ self.backend.playlists.set_dummy_playlists([
Playlist(name='a\n', uri='dummy:')]) Playlist(name='a\n', uri='dummy:')])
self.send_request('listplaylists') self.send_request('listplaylists')
self.assertInResponse('playlist: a ') self.assertInResponse('playlist: a ')
self.assertNotInResponse('playlist: a\n') self.assertNotInResponse('playlist: a\n')
self.assertInResponse('OK') self.assertInResponse('OK')
@ -121,7 +134,9 @@ class PlaylistsHandlerTest(protocol.BaseTestCase):
def test_listplaylists_replaces_carriage_return_with_space(self): def test_listplaylists_replaces_carriage_return_with_space(self):
self.backend.playlists.set_dummy_playlists([ self.backend.playlists.set_dummy_playlists([
Playlist(name='a\r', uri='dummy:')]) Playlist(name='a\r', uri='dummy:')])
self.send_request('listplaylists') self.send_request('listplaylists')
self.assertInResponse('playlist: a ') self.assertInResponse('playlist: a ')
self.assertNotInResponse('playlist: a\r') self.assertNotInResponse('playlist: a\r')
self.assertInResponse('OK') self.assertInResponse('OK')
@ -129,7 +144,9 @@ class PlaylistsHandlerTest(protocol.BaseTestCase):
def test_listplaylists_replaces_forward_slash_with_pipe(self): def test_listplaylists_replaces_forward_slash_with_pipe(self):
self.backend.playlists.set_dummy_playlists([ self.backend.playlists.set_dummy_playlists([
Playlist(name='a/b', uri='dummy:')]) Playlist(name='a/b', uri='dummy:')])
self.send_request('listplaylists') self.send_request('listplaylists')
self.assertInResponse('playlist: a|b') self.assertInResponse('playlist: a|b')
self.assertNotInResponse('playlist: a/b') self.assertNotInResponse('playlist: a/b')
self.assertInResponse('OK') self.assertInResponse('OK')
@ -211,6 +228,7 @@ class PlaylistsHandlerTest(protocol.BaseTestCase):
def test_load_unknown_playlist_acks(self): def test_load_unknown_playlist_acks(self):
self.send_request('load "unknown playlist"') self.send_request('load "unknown playlist"')
self.assertEqual(0, len(self.core.tracklist.tracks.get())) self.assertEqual(0, len(self.core.tracklist.tracks.get()))
self.assertEqualResponse('ACK [50@0] {load} No such playlist') self.assertEqualResponse('ACK [50@0] {load} No such playlist')
@ -235,10 +253,11 @@ class PlaylistsHandlerTest(protocol.BaseTestCase):
Track(uri='dummy:a'), Track(uri='dummy:a'),
] ]
self.backend.library.dummy_library = tracks self.backend.library.dummy_library = tracks
self.send_request('playlistadd "name" "dummy:a"') self.send_request('playlistadd "name" "dummy:a"')
self.assertInResponse('OK') self.assertInResponse('OK')
self.assertNotEqual( self.assertIsNotNone(self.backend.playlists.lookup('dummy:name').get())
None, self.backend.playlists.lookup('dummy:name').get())
def test_playlistclear(self): def test_playlistclear(self):
self.backend.playlists.set_dummy_playlists([ self.backend.playlists.set_dummy_playlists([
@ -246,15 +265,16 @@ class PlaylistsHandlerTest(protocol.BaseTestCase):
name='name', uri='dummy:a1', tracks=[Track(uri='b')])]) name='name', uri='dummy:a1', tracks=[Track(uri='b')])])
self.send_request('playlistclear "name"') self.send_request('playlistclear "name"')
self.assertInResponse('OK') self.assertInResponse('OK')
self.assertEqual( self.assertEqual(
0, len(self.backend.playlists.get_items("dummy:a1").get())) 0, len(self.backend.playlists.get_items('dummy:a1').get()))
def test_playlistclear_creates_playlist(self): def test_playlistclear_creates_playlist(self):
self.send_request('playlistclear "name"') self.send_request('playlistclear "name"')
self.assertInResponse('OK') self.assertInResponse('OK')
self.assertNotEqual( self.assertIsNotNone(self.backend.playlists.lookup('dummy:name').get())
None, self.backend.playlists.lookup("dummy:name").get())
def test_playlistdelete(self): def test_playlistdelete(self):
tracks = [ tracks = [
@ -267,9 +287,10 @@ class PlaylistsHandlerTest(protocol.BaseTestCase):
name='name', uri='dummy:a1', tracks=tracks)]) name='name', uri='dummy:a1', tracks=tracks)])
self.send_request('playlistdelete "name" "2"') self.send_request('playlistdelete "name" "2"')
self.assertInResponse('OK') self.assertInResponse('OK')
self.assertEqual( self.assertEqual(
2, len(self.backend.playlists.get_items("dummy:a1").get())) 2, len(self.backend.playlists.get_items('dummy:a1').get()))
def test_playlistmove(self): def test_playlistmove(self):
tracks = [ tracks = [
@ -280,32 +301,37 @@ class PlaylistsHandlerTest(protocol.BaseTestCase):
self.backend.playlists.set_dummy_playlists([ self.backend.playlists.set_dummy_playlists([
Playlist( Playlist(
name='name', uri='dummy:a1', tracks=tracks)]) name='name', uri='dummy:a1', tracks=tracks)])
self.send_request('playlistmove "name" "2" "0"') self.send_request('playlistmove "name" "2" "0"')
self.assertInResponse('OK') self.assertInResponse('OK')
self.assertEqual( self.assertEqual(
"dummy:c", "dummy:c",
self.backend.playlists.get_items("dummy:a1").get()[0].uri) self.backend.playlists.get_items('dummy:a1').get()[0].uri)
def test_rename(self): def test_rename(self):
self.backend.playlists.set_dummy_playlists([ self.backend.playlists.set_dummy_playlists([
Playlist( Playlist(
name='old_name', uri='dummy:a1', tracks=[Track(uri='b')])]) name='old_name', uri='dummy:a1', tracks=[Track(uri='b')])])
self.send_request('rename "old_name" "new_name"') self.send_request('rename "old_name" "new_name"')
self.assertInResponse('OK') self.assertInResponse('OK')
self.assertIsNotNone( self.assertIsNotNone(
self.backend.playlists.lookup("dummy:new_name").get()) self.backend.playlists.lookup('dummy:new_name').get())
def test_rm(self): def test_rm(self):
self.backend.playlists.set_dummy_playlists([ self.backend.playlists.set_dummy_playlists([
Playlist( Playlist(
name='name', uri='dummy:a1', tracks=[Track(uri='b')])]) name='name', uri='dummy:a1', tracks=[Track(uri='b')])])
self.send_request('rm "name"') self.send_request('rm "name"')
self.assertInResponse('OK') self.assertInResponse('OK')
self.assertEqual( self.assertIsNone(self.backend.playlists.lookup('dummy:a1').get())
None, self.backend.playlists.lookup("dummy:a1").get())
def test_save(self): def test_save(self):
self.send_request('save "name"') self.send_request('save "name"')
self.assertInResponse('OK') self.assertInResponse('OK')
self.assertNotEqual( self.assertIsNotNone(self.backend.playlists.lookup('dummy:name').get())
None, self.backend.playlists.lookup("dummy:name").get())