Merge pull request #287 from jodal/feature/search-result-model

Add SearchResult model
This commit is contained in:
Stein Magnus Jodal 2012-12-23 09:47:11 -08:00
commit 58cbbe8cb3
11 changed files with 235 additions and 117 deletions

View File

@ -76,8 +76,17 @@ v0.11.0 (in development)
- Specified that :attr:`mopidy.models.Playlist.last_modified` should be in UTC.
- Added :class:`mopidy.models.SearchResult` model to encapsulate search results
consisting of more than just tracks.
*Core API:*
- Change the following methods to return :class:`mopidy.models.SearchResult`
objects which can include both track results and other results:
- :meth:`mopidy.core.LibraryController.find_exact`
- :meth:`mopidy.core.LibraryController.search`
- Change the following methods to accept either a dict with filters or kwargs.
Previously they only accepted kwargs, which made them impossible to use from
the Mopidy.js through JSON-RPC, which doesn't support kwargs.

View File

@ -19,7 +19,7 @@ from __future__ import unicode_literals
import pykka
from mopidy.backends import base
from mopidy.models import Playlist
from mopidy.models import Playlist, SearchResult
class DummyBackend(pykka.ThreadingActor, base.Backend):
@ -37,8 +37,8 @@ class DummyLibraryProvider(base.BaseLibraryProvider):
def __init__(self, *args, **kwargs):
super(DummyLibraryProvider, self).__init__(*args, **kwargs)
self.dummy_library = []
self.dummy_find_exact_result = []
self.dummy_search_result = []
self.dummy_find_exact_result = SearchResult()
self.dummy_search_result = SearchResult()
def find_exact(self, **query):
return self.dummy_find_exact_result

View File

@ -4,7 +4,7 @@ import logging
from mopidy import settings
from mopidy.backends import base
from mopidy.models import Album
from mopidy.models import Album, SearchResult
from .translator import parse_mpd_tag_cache
@ -70,7 +70,7 @@ class LocalLibraryProvider(base.BaseLibraryProvider):
result_tracks = filter(any_filter, result_tracks)
else:
raise LookupError('Invalid lookup field: %s' % field)
return result_tracks
return SearchResult(uri='file:search', tracks=result_tracks)
def search(self, **query):
self._validate_query(query)
@ -107,7 +107,7 @@ class LocalLibraryProvider(base.BaseLibraryProvider):
result_tracks = filter(any_filter, result_tracks)
else:
raise LookupError('Invalid lookup field: %s' % field)
return result_tracks
return SearchResult(uri='file:search', tracks=result_tracks)
def _validate_query(self, query):
for (_, values) in query.iteritems():

View File

@ -2,13 +2,14 @@ from __future__ import unicode_literals
import logging
import time
import urllib
import pykka
from spotify import Link, SpotifyError
from mopidy import settings
from mopidy.backends import base
from mopidy.models import Track
from mopidy.models import Track, SearchResult
from . import translator
@ -123,12 +124,16 @@ class SpotifyLibraryProvider(base.BaseLibraryProvider):
if not query:
return self._get_all_tracks()
if 'uri' in query.keys():
result = []
for uri in query['uri']:
tracks = self.lookup(uri)
result += tracks
return result
uris = query.get('uri', [])
if uris:
tracks = []
for uri in uris:
tracks += self.lookup(uri)
if len(uris) == 1:
uri = uris[0]
else:
uri = 'spotify:search'
return SearchResult(uri=uri, tracks=tracks)
spotify_query = self._translate_search_query(query)
logger.debug('Spotify search query: %s' % spotify_query)
@ -136,12 +141,16 @@ class SpotifyLibraryProvider(base.BaseLibraryProvider):
future = pykka.ThreadingFuture()
def callback(results, userdata=None):
# TODO Include results from results.albums(), etc. too
# TODO Consider launching a second search if results.total_tracks()
# is larger than len(results.tracks())
tracks = [
translator.to_mopidy_track(t) for t in results.tracks()]
future.set(tracks)
search_result = SearchResult(
uri='spotify:search:%s' % (
urllib.quote(results.query().encode('utf-8'))),
albums=[
translator.to_mopidy_album(a) for a in results.albums()],
artists=[
translator.to_mopidy_artist(a) for a in results.artists()],
tracks=[
translator.to_mopidy_track(t) for t in results.tracks()])
future.set(search_result)
if not self.backend.spotify.connected.wait(settings.SPOTIFY_TIMEOUT):
logger.debug('Not connected: Spotify search cancelled')
@ -149,7 +158,7 @@ class SpotifyLibraryProvider(base.BaseLibraryProvider):
self.backend.spotify.session.search(
spotify_query, callback,
track_count=200, album_count=0, artist_count=0)
album_count=200, artist_count=200, track_count=200)
try:
return future.get(timeout=settings.SPOTIFY_TIMEOUT)
@ -157,7 +166,7 @@ class SpotifyLibraryProvider(base.BaseLibraryProvider):
logger.debug(
'Timeout: Spotify search did not return in %ds',
settings.SPOTIFY_TIMEOUT)
return []
return SearchResult(uri='spotify:search')
def _get_all_tracks(self):
# Since we can't search for the entire Spotify library, we return
@ -165,7 +174,7 @@ class SpotifyLibraryProvider(base.BaseLibraryProvider):
tracks = []
for playlist in self.backend.playlists.playlists:
tracks += playlist.tracks
return tracks
return SearchResult(uri='spotify:search', tracks=tracks)
def _translate_search_query(self, mopidy_query):
spotify_query = []

View File

@ -1,6 +1,5 @@
from __future__ import unicode_literals
import itertools
import urlparse
import pykka
@ -37,13 +36,12 @@ class LibraryController(object):
:param query: one or more queries to search for
:type query: dict
:rtype: list of :class:`mopidy.models.Track`
:rtype: list of :class:`mopidy.models.SearchResult`
"""
query = query or kwargs
futures = [
b.library.find_exact(**query) for b in self.backends.with_library]
results = pykka.get_all(futures)
return list(itertools.chain(*results))
return pykka.get_all(futures)
def lookup(self, uri):
"""
@ -98,10 +96,9 @@ class LibraryController(object):
:param query: one or more queries to search for
:type query: dict
:rtype: list of :class:`mopidy.models.Track`
:rtype: list of :class:`mopidy.models.SearchResult`
"""
query = query or kwargs
futures = [
b.library.search(**query) for b in self.backends.with_library]
results = pykka.get_all(futures)
return list(itertools.chain(*results))
return pykka.get_all(futures)

View File

@ -1,5 +1,7 @@
from __future__ import unicode_literals
import itertools
from mopidy.frontends.mpd import translator
from mopidy.frontends.mpd.exceptions import MpdNotImplemented
from mopidy.frontends.mpd.protocol import handle_request, stored_playlists
@ -10,6 +12,10 @@ QUERY_RE = (
r'[Tt]itle|[Aa]ny)"? "[^"]*"\s?)+)$')
def _get_tracks(search_results):
return list(itertools.chain(*[r.tracks for r in search_results]))
@handle_request(r'^count "(?P<tag>[^"]+)" "(?P<needle>[^"]*)"$')
def count(context, tag, needle):
"""
@ -55,8 +61,8 @@ def find(context, mpd_query):
query = translator.query_from_mpd_search_format(mpd_query)
except ValueError:
return
result = context.core.library.find_exact(**query).get()
return translator.tracks_to_mpd_format(result)
results = context.core.library.find_exact(**query).get()
return translator.tracks_to_mpd_format(_get_tracks(results))
@handle_request(r'^findadd ' + QUERY_RE)
@ -73,8 +79,8 @@ def findadd(context, mpd_query):
query = translator.query_from_mpd_search_format(mpd_query)
except ValueError:
return
result = context.core.library.find_exact(**query).get()
context.core.tracklist.add(result)
results = context.core.library.find_exact(**query).get()
context.core.tracklist.add(_get_tracks(results))
@handle_request(
@ -179,8 +185,8 @@ def list_(context, field, mpd_query=None):
def _list_artist(context, query):
artists = set()
tracks = context.core.library.find_exact(**query).get()
for track in tracks:
results = context.core.library.find_exact(**query).get()
for track in _get_tracks(results):
for artist in track.artists:
if artist.name:
artists.add(('Artist', artist.name))
@ -189,8 +195,8 @@ def _list_artist(context, query):
def _list_album(context, query):
albums = set()
tracks = context.core.library.find_exact(**query).get()
for track in tracks:
results = context.core.library.find_exact(**query).get()
for track in _get_tracks(results):
if track.album and track.album.name:
albums.add(('Album', track.album.name))
return albums
@ -198,8 +204,8 @@ def _list_album(context, query):
def _list_date(context, query):
dates = set()
tracks = context.core.library.find_exact(**query).get()
for track in tracks:
results = context.core.library.find_exact(**query).get()
for track in _get_tracks(results):
if track.date:
dates.add(('Date', track.date))
return dates
@ -297,8 +303,8 @@ def search(context, mpd_query):
query = translator.query_from_mpd_search_format(mpd_query)
except ValueError:
return
result = context.core.library.search(**query).get()
return translator.tracks_to_mpd_format(result)
results = context.core.library.search(**query).get()
return translator.tracks_to_mpd_format(_get_tracks(results))
@handle_request(r'^searchadd ' + QUERY_RE)
@ -318,8 +324,8 @@ def searchadd(context, mpd_query):
query = translator.query_from_mpd_search_format(mpd_query)
except ValueError:
return
result = context.core.library.search(**query).get()
context.core.tracklist.add(result)
results = context.core.library.search(**query).get()
context.core.tracklist.add(_get_tracks(results))
@handle_request(r'^searchaddpl "(?P<playlist_name>[^"]+)" ' + QUERY_RE)
@ -341,14 +347,14 @@ def searchaddpl(context, playlist_name, mpd_query):
query = translator.query_from_mpd_search_format(mpd_query)
except ValueError:
return
result = context.core.library.search(**query).get()
results = context.core.library.search(**query).get()
playlists = context.core.playlists.filter(name=playlist_name).get()
if playlists:
playlist = playlists[0]
else:
playlist = context.core.playlists.create(playlist_name).get()
tracks = list(playlist.tracks) + result
tracks = list(playlist.tracks) + _get_tracks(results)
playlist = playlist.copy(tracks=tracks)
context.core.playlists.save(playlist)

View File

@ -318,3 +318,34 @@ class Playlist(ImmutableObject):
def length(self):
"""The number of tracks in the playlist. Read-only."""
return len(self.tracks)
class SearchResult(ImmutableObject):
"""
:param uri: search result URI
:type uri: string
:param tracks: matching tracks
:type tracks: list of :class:`Track` elements
:param artists: matching artists
:type artists: list of :class:`Artist` elements
:param albums: matching albums
:type albums: list of :class:`Album` elements
"""
# The search result URI. Read-only.
uri = None
# The tracks matching the search query. Read-only.
tracks = tuple()
# The artists matching the search query. Read-only.
artists = tuple()
# The albums matching the search query. Read-only.
albums = tuple()
def __init__(self, *args, **kwargs):
self.__dict__['tracks'] = tuple(kwargs.pop('tracks', []))
self.__dict__['artists'] = tuple(kwargs.pop('artists', []))
self.__dict__['albums'] = tuple(kwargs.pop('albums', []))
super(SearchResult, self).__init__(*args, **kwargs)

View File

@ -53,53 +53,53 @@ class LibraryControllerTest(object):
def test_find_exact_no_hits(self):
result = self.library.find_exact(track=['unknown track'])
self.assertEqual(result, [])
self.assertEqual(list(result[0].tracks), [])
result = self.library.find_exact(artist=['unknown artist'])
self.assertEqual(result, [])
self.assertEqual(list(result[0].tracks), [])
result = self.library.find_exact(album=['unknown artist'])
self.assertEqual(result, [])
self.assertEqual(list(result[0].tracks), [])
def test_find_exact_uri(self):
track_1_uri = 'file://' + path_to_data_dir('uri1')
result = self.library.find_exact(uri=track_1_uri)
self.assertEqual(result, self.tracks[:1])
self.assertEqual(list(result[0].tracks), self.tracks[:1])
track_2_uri = 'file://' + path_to_data_dir('uri2')
result = self.library.find_exact(uri=track_2_uri)
self.assertEqual(result, self.tracks[1:2])
self.assertEqual(list(result[0].tracks), self.tracks[1:2])
def test_find_exact_track(self):
result = self.library.find_exact(track=['track1'])
self.assertEqual(result, self.tracks[:1])
self.assertEqual(list(result[0].tracks), self.tracks[:1])
result = self.library.find_exact(track=['track2'])
self.assertEqual(result, self.tracks[1:2])
self.assertEqual(list(result[0].tracks), self.tracks[1:2])
def test_find_exact_artist(self):
result = self.library.find_exact(artist=['artist1'])
self.assertEqual(result, self.tracks[:1])
self.assertEqual(list(result[0].tracks), self.tracks[:1])
result = self.library.find_exact(artist=['artist2'])
self.assertEqual(result, self.tracks[1:2])
self.assertEqual(list(result[0].tracks), self.tracks[1:2])
def test_find_exact_album(self):
result = self.library.find_exact(album=['album1'])
self.assertEqual(result, self.tracks[:1])
self.assertEqual(list(result[0].tracks), self.tracks[:1])
result = self.library.find_exact(album=['album2'])
self.assertEqual(result, self.tracks[1:2])
self.assertEqual(list(result[0].tracks), self.tracks[1:2])
def test_find_exact_date(self):
result = self.library.find_exact(date=['2001'])
self.assertEqual(result, [])
self.assertEqual(list(result[0].tracks), [])
result = self.library.find_exact(date=['2001-02-03'])
self.assertEqual(result, self.tracks[:1])
self.assertEqual(list(result[0].tracks), self.tracks[:1])
result = self.library.find_exact(date=['2002'])
self.assertEqual(result, self.tracks[1:2])
self.assertEqual(list(result[0].tracks), self.tracks[1:2])
def test_find_exact_wrong_type(self):
test = lambda: self.library.find_exact(wrong=['test'])
@ -117,70 +117,70 @@ class LibraryControllerTest(object):
def test_search_no_hits(self):
result = self.library.search(track=['unknown track'])
self.assertEqual(result, [])
self.assertEqual(list(result[0].tracks), [])
result = self.library.search(artist=['unknown artist'])
self.assertEqual(result, [])
self.assertEqual(list(result[0].tracks), [])
result = self.library.search(album=['unknown artist'])
self.assertEqual(result, [])
self.assertEqual(list(result[0].tracks), [])
result = self.library.search(uri=['unknown'])
self.assertEqual(result, [])
self.assertEqual(list(result[0].tracks), [])
result = self.library.search(any=['unknown'])
self.assertEqual(result, [])
self.assertEqual(list(result[0].tracks), [])
def test_search_uri(self):
result = self.library.search(uri=['RI1'])
self.assertEqual(result, self.tracks[:1])
self.assertEqual(list(result[0].tracks), self.tracks[:1])
result = self.library.search(uri=['RI2'])
self.assertEqual(result, self.tracks[1:2])
self.assertEqual(list(result[0].tracks), self.tracks[1:2])
def test_search_track(self):
result = self.library.search(track=['Rack1'])
self.assertEqual(result, self.tracks[:1])
self.assertEqual(list(result[0].tracks), self.tracks[:1])
result = self.library.search(track=['Rack2'])
self.assertEqual(result, self.tracks[1:2])
self.assertEqual(list(result[0].tracks), self.tracks[1:2])
def test_search_artist(self):
result = self.library.search(artist=['Tist1'])
self.assertEqual(result, self.tracks[:1])
self.assertEqual(list(result[0].tracks), self.tracks[:1])
result = self.library.search(artist=['Tist2'])
self.assertEqual(result, self.tracks[1:2])
self.assertEqual(list(result[0].tracks), self.tracks[1:2])
def test_search_album(self):
result = self.library.search(album=['Bum1'])
self.assertEqual(result, self.tracks[:1])
self.assertEqual(list(result[0].tracks), self.tracks[:1])
result = self.library.search(album=['Bum2'])
self.assertEqual(result, self.tracks[1:2])
self.assertEqual(list(result[0].tracks), self.tracks[1:2])
def test_search_date(self):
result = self.library.search(date=['2001'])
self.assertEqual(result, self.tracks[:1])
self.assertEqual(list(result[0].tracks), self.tracks[:1])
result = self.library.search(date=['2001-02-03'])
self.assertEqual(result, self.tracks[:1])
self.assertEqual(list(result[0].tracks), self.tracks[:1])
result = self.library.search(date=['2001-02-04'])
self.assertEqual(result, [])
self.assertEqual(list(result[0].tracks), [])
result = self.library.search(date=['2002'])
self.assertEqual(result, self.tracks[1:2])
self.assertEqual(list(result[0].tracks), self.tracks[1:2])
def test_search_any(self):
result = self.library.search(any=['Tist1'])
self.assertEqual(result, self.tracks[:1])
self.assertEqual(list(result[0].tracks), self.tracks[:1])
result = self.library.search(any=['Rack1'])
self.assertEqual(result, self.tracks[:1])
self.assertEqual(list(result[0].tracks), self.tracks[:1])
result = self.library.search(any=['Bum1'])
self.assertEqual(result, self.tracks[:1])
self.assertEqual(list(result[0].tracks), self.tracks[:1])
result = self.library.search(any=['RI1'])
self.assertEqual(result, self.tracks[:1])
self.assertEqual(list(result[0].tracks), self.tracks[:1])
def test_search_wrong_type(self):
test = lambda: self.library.search(wrong=['test'])

View File

@ -4,7 +4,7 @@ import mock
from mopidy.backends import base
from mopidy.core import Core
from mopidy.models import Track
from mopidy.models import SearchResult, Track
from tests import unittest
@ -75,59 +75,71 @@ class CoreLibraryTest(unittest.TestCase):
def test_find_exact_combines_results_from_all_backends(self):
track1 = Track(uri='dummy1:a')
track2 = Track(uri='dummy2:a')
self.library1.find_exact().get.return_value = [track1]
result1 = SearchResult(tracks=[track1])
result2 = SearchResult(tracks=[track2])
self.library1.find_exact().get.return_value = result1
self.library1.find_exact.reset_mock()
self.library2.find_exact().get.return_value = [track2]
self.library2.find_exact().get.return_value = result2
self.library2.find_exact.reset_mock()
result = self.core.library.find_exact(any=['a'])
self.assertIn(track1, result)
self.assertIn(track2, result)
self.assertIn(result1, result)
self.assertIn(result2, result)
self.library1.find_exact.assert_called_once_with(any=['a'])
self.library2.find_exact.assert_called_once_with(any=['a'])
def test_find_accepts_query_dict_instead_of_kwargs(self):
track1 = Track(uri='dummy1:a')
track2 = Track(uri='dummy2:a')
self.library1.find_exact().get.return_value = [track1]
result1 = SearchResult(tracks=[track1])
result2 = SearchResult(tracks=[track2])
self.library1.find_exact().get.return_value = result1
self.library1.find_exact.reset_mock()
self.library2.find_exact().get.return_value = [track2]
self.library2.find_exact().get.return_value = result2
self.library2.find_exact.reset_mock()
result = self.core.library.find_exact(dict(any=['a']))
self.assertIn(track1, result)
self.assertIn(track2, result)
self.assertIn(result1, result)
self.assertIn(result2, result)
self.library1.find_exact.assert_called_once_with(any=['a'])
self.library2.find_exact.assert_called_once_with(any=['a'])
def test_search_combines_results_from_all_backends(self):
track1 = Track(uri='dummy1:a')
track2 = Track(uri='dummy2:a')
self.library1.search().get.return_value = [track1]
result1 = SearchResult(tracks=[track1])
result2 = SearchResult(tracks=[track2])
self.library1.search().get.return_value = result1
self.library1.search.reset_mock()
self.library2.search().get.return_value = [track2]
self.library2.search().get.return_value = result2
self.library2.search.reset_mock()
result = self.core.library.search(any=['a'])
self.assertIn(track1, result)
self.assertIn(track2, result)
self.assertIn(result1, result)
self.assertIn(result2, result)
self.library1.search.assert_called_once_with(any=['a'])
self.library2.search.assert_called_once_with(any=['a'])
def test_search_accepts_query_dict_instead_of_kwargs(self):
track1 = Track(uri='dummy1:a')
track2 = Track(uri='dummy2:a')
self.library1.search().get.return_value = [track1]
result1 = SearchResult(tracks=[track1])
result2 = SearchResult(tracks=[track2])
self.library1.search().get.return_value = result1
self.library1.search.reset_mock()
self.library2.search().get.return_value = [track2]
self.library2.search().get.return_value = result2
self.library2.search.reset_mock()
result = self.core.library.search(dict(any=['a']))
self.assertIn(track1, result)
self.assertIn(track2, result)
self.assertIn(result1, result)
self.assertIn(result2, result)
self.library1.search.assert_called_once_with(any=['a'])
self.library2.search.assert_called_once_with(any=['a'])

View File

@ -1,6 +1,6 @@
from __future__ import unicode_literals
from mopidy.models import Album, Artist, Track
from mopidy.models import Album, Artist, SearchResult, Track
from tests.frontends.mpd import protocol
@ -13,9 +13,8 @@ class MusicDatabaseHandlerTest(protocol.BaseTestCase):
self.assertInResponse('OK')
def test_findadd(self):
self.backend.library.dummy_find_exact_result = [
Track(uri='dummy:a', name='A'),
]
self.backend.library.dummy_find_exact_result = SearchResult(
tracks=[Track(uri='dummy:a', name='A')])
self.assertEqual(self.core.tracklist.length.get(), 0)
self.sendRequest('findadd "title" "A"')
@ -25,9 +24,8 @@ class MusicDatabaseHandlerTest(protocol.BaseTestCase):
self.assertInResponse('OK')
def test_searchadd(self):
self.backend.library.dummy_search_result = [
Track(uri='dummy:a', name='A'),
]
self.backend.library.dummy_search_result = SearchResult(
tracks=[Track(uri='dummy:a', name='A')])
self.assertEqual(self.core.tracklist.length.get(), 0)
self.sendRequest('searchadd "title" "a"')
@ -43,9 +41,8 @@ class MusicDatabaseHandlerTest(protocol.BaseTestCase):
Track(uri='dummy:y', name='y'),
])
self.core.playlists.save(playlist)
self.backend.library.dummy_search_result = [
Track(uri='dummy:a', name='A'),
]
self.backend.library.dummy_search_result = SearchResult(
tracks=[Track(uri='dummy:a', name='A')])
playlists = self.core.playlists.filter(name='my favs').get()
self.assertEqual(len(playlists), 1)
self.assertEqual(len(playlists[0].tracks), 2)
@ -61,9 +58,8 @@ class MusicDatabaseHandlerTest(protocol.BaseTestCase):
self.assertInResponse('OK')
def test_searchaddpl_creates_missing_playlist(self):
self.backend.library.dummy_search_result = [
Track(uri='dummy:a', name='A'),
]
self.backend.library.dummy_search_result = SearchResult(
tracks=[Track(uri='dummy:a', name='A')])
self.assertEqual(
len(self.core.playlists.filter(name='my favs').get()), 0)
@ -185,6 +181,17 @@ class MusicDatabaseFindTest(protocol.BaseTestCase):
class MusicDatabaseListTest(protocol.BaseTestCase):
def test_list(self):
self.backend.library.dummy_find_exact_result = SearchResult(
tracks=[
Track(uri='dummy:a', name='A', artists=[
Artist(name='A Artist')])])
self.sendRequest('list "artist" "artist" "foo"')
self.assertInResponse('Artist: A Artist')
self.assertInResponse('OK')
def test_list_foo_returns_ack(self):
self.sendRequest('list "foo"')
self.assertEqualResponse('ACK [2@0] {list} incorrect arguments')
@ -242,8 +249,8 @@ class MusicDatabaseListTest(protocol.BaseTestCase):
self.assertInResponse('OK')
def test_list_artist_should_not_return_artists_without_names(self):
self.backend.library.dummy_find_exact_result = [
Track(artists=[Artist(name='')])]
self.backend.library.dummy_find_exact_result = SearchResult(
tracks=[Track(artists=[Artist(name='')])])
self.sendRequest('list "artist"')
self.assertNotInResponse('Artist: ')
@ -301,8 +308,8 @@ class MusicDatabaseListTest(protocol.BaseTestCase):
self.assertInResponse('OK')
def test_list_album_should_not_return_albums_without_names(self):
self.backend.library.dummy_find_exact_result = [
Track(album=Album(name=''))]
self.backend.library.dummy_find_exact_result = SearchResult(
tracks=[Track(album=Album(name=''))])
self.sendRequest('list "album"')
self.assertNotInResponse('Album: ')
@ -356,7 +363,8 @@ class MusicDatabaseListTest(protocol.BaseTestCase):
self.assertInResponse('OK')
def test_list_date_should_not_return_blank_dates(self):
self.backend.library.dummy_find_exact_result = [Track(date='')]
self.backend.library.dummy_find_exact_result = SearchResult(
tracks=[Track(date='')])
self.sendRequest('list "date"')
self.assertNotInResponse('Date: ')

View File

@ -4,7 +4,7 @@ import datetime
import json
from mopidy.models import (
Artist, Album, TlTrack, Track, Playlist,
Artist, Album, TlTrack, Track, Playlist, SearchResult,
ModelJSONEncoder, model_json_decoder)
from tests import unittest
@ -862,10 +862,56 @@ class PlaylistTest(unittest.TestCase):
def test_ne(self):
playlist1 = Playlist(
uri='uri1', name='name2', tracks=[Track(uri='uri1')],
uri='uri1', name='name1', tracks=[Track(uri='uri1')],
last_modified=1)
playlist2 = Playlist(
uri='uri2', name='name2', tracks=[Track(uri='uri2')],
last_modified=2)
self.assertNotEqual(playlist1, playlist2)
self.assertNotEqual(hash(playlist1), hash(playlist2))
class SearchResultTest(unittest.TestCase):
def test_uri(self):
uri = 'an_uri'
result = SearchResult(uri=uri)
self.assertEqual(result.uri, uri)
self.assertRaises(AttributeError, setattr, result, 'uri', None)
def test_tracks(self):
tracks = [Track(), Track(), Track()]
result = SearchResult(tracks=tracks)
self.assertEqual(list(result.tracks), tracks)
self.assertRaises(AttributeError, setattr, result, 'tracks', None)
def test_artists(self):
artists = [Artist(), Artist(), Artist()]
result = SearchResult(artists=artists)
self.assertEqual(list(result.artists), artists)
self.assertRaises(AttributeError, setattr, result, 'artists', None)
def test_albums(self):
albums = [Album(), Album(), Album()]
result = SearchResult(albums=albums)
self.assertEqual(list(result.albums), albums)
self.assertRaises(AttributeError, setattr, result, 'albums', None)
def test_invalid_kwarg(self):
test = lambda: SearchResult(foo='baz')
self.assertRaises(TypeError, test)
def test_repr_without_results(self):
self.assertEquals(
"SearchResult(albums=[], artists=[], tracks=[], uri=u'uri')",
repr(SearchResult(uri='uri')))
def test_serialize_without_results(self):
self.assertDictEqual(
{'__model__': 'SearchResult', 'uri': 'uri'},
SearchResult(uri='uri').serialize())
def test_to_json_and_back(self):
result1 = SearchResult(uri='uri')
serialized = json.dumps(result1, cls=ModelJSONEncoder)
result2 = json.loads(serialized, object_hook=model_json_decoder)
self.assertEqual(result1, result2)