Merge branch 'develop' into feature/mpd-search-by-albumartist
Conflicts: mopidy/frontends/mpd/protocol/music_db.py mopidy/frontends/mpd/translator.py
This commit is contained in:
commit
9064fc02fd
@ -27,9 +27,14 @@ class LocalLibraryProvider(base.BaseLibraryProvider):
|
||||
self._media_dir, self._tag_cache_file)
|
||||
|
||||
tracks = parse_mpd_tag_cache(self._tag_cache_file, self._media_dir)
|
||||
uris_to_remove = set(self._uri_mapping)
|
||||
|
||||
for track in tracks:
|
||||
self._uri_mapping[track.uri] = track
|
||||
uris_to_remove.discard(track.uri)
|
||||
|
||||
for uri in uris_to_remove:
|
||||
del self._uri_mapping[uri]
|
||||
|
||||
logger.info(
|
||||
'Loaded %d local tracks from %s using %s',
|
||||
@ -55,7 +60,10 @@ class LocalLibraryProvider(base.BaseLibraryProvider):
|
||||
values = [values]
|
||||
# FIXME this is bound to be slow for large libraries
|
||||
for value in values:
|
||||
q = value.strip()
|
||||
if field == 'track_no':
|
||||
q = value
|
||||
else:
|
||||
q = value.strip()
|
||||
|
||||
uri_filter = lambda t: q == t.uri
|
||||
track_filter = lambda t: q == t.name
|
||||
@ -65,13 +73,16 @@ class LocalLibraryProvider(base.BaseLibraryProvider):
|
||||
albumartist_filter = lambda t: any([
|
||||
q == a.name
|
||||
for a in getattr(t.album, 'artists', [])])
|
||||
track_no_filter = lambda t: q == t.track_no
|
||||
date_filter = lambda t: q == t.date
|
||||
any_filter = lambda t: (
|
||||
uri_filter(t) or
|
||||
track_filter(t) or
|
||||
album_filter(t) or
|
||||
artist_filter(t) or
|
||||
albumartist_filter(t) or
|
||||
uri_filter(t))
|
||||
track_no_filter(t) or
|
||||
date_filter(t))
|
||||
|
||||
if field == 'uri':
|
||||
result_tracks = filter(uri_filter, result_tracks)
|
||||
@ -83,6 +94,8 @@ class LocalLibraryProvider(base.BaseLibraryProvider):
|
||||
result_tracks = filter(artist_filter, result_tracks)
|
||||
elif field == 'albumartist':
|
||||
result_tracks = filter(albumartist_filter, result_tracks)
|
||||
elif field == 'track_no':
|
||||
result_tracks = filter(track_no_filter, result_tracks)
|
||||
elif field == 'date':
|
||||
result_tracks = filter(date_filter, result_tracks)
|
||||
elif field == 'any':
|
||||
@ -105,7 +118,10 @@ class LocalLibraryProvider(base.BaseLibraryProvider):
|
||||
values = [values]
|
||||
# FIXME this is bound to be slow for large libraries
|
||||
for value in values:
|
||||
q = value.strip().lower()
|
||||
if field == 'track_no':
|
||||
q = value
|
||||
else:
|
||||
q = value.strip().lower()
|
||||
|
||||
uri_filter = lambda t: q in t.uri.lower()
|
||||
track_filter = lambda t: q in t.name.lower()
|
||||
@ -116,13 +132,16 @@ class LocalLibraryProvider(base.BaseLibraryProvider):
|
||||
albumartist_filter = lambda t: any([
|
||||
q in a.name.lower()
|
||||
for a in getattr(t.album, 'artists', [])])
|
||||
track_no_filter = lambda t: q == t.track_no
|
||||
date_filter = lambda t: t.date and t.date.startswith(q)
|
||||
any_filter = lambda t: (
|
||||
uri_filter(t) or
|
||||
track_filter(t) or
|
||||
album_filter(t) or
|
||||
artist_filter(t) or
|
||||
albumartist_filter(t) or
|
||||
uri_filter(t))
|
||||
track_no_filter(t) or
|
||||
date_filter(t))
|
||||
|
||||
if field == 'uri':
|
||||
result_tracks = filter(uri_filter, result_tracks)
|
||||
@ -134,6 +153,8 @@ class LocalLibraryProvider(base.BaseLibraryProvider):
|
||||
result_tracks = filter(artist_filter, result_tracks)
|
||||
elif field == 'albumartist':
|
||||
result_tracks = filter(albumartist_filter, result_tracks)
|
||||
elif field == 'track_no':
|
||||
result_tracks = filter(track_no_filter, result_tracks)
|
||||
elif field == 'date':
|
||||
result_tracks = filter(date_filter, result_tracks)
|
||||
elif field == 'any':
|
||||
|
||||
@ -2,8 +2,10 @@ from __future__ import unicode_literals
|
||||
|
||||
import ConfigParser as configparser
|
||||
import io
|
||||
import itertools
|
||||
import logging
|
||||
import os.path
|
||||
import re
|
||||
|
||||
from mopidy.config import keyring
|
||||
from mopidy.config.schemas import * # noqa
|
||||
@ -145,6 +147,53 @@ def _format(config, comments, schemas, display):
|
||||
return b'\n'.join(output)
|
||||
|
||||
|
||||
def _preprocess(config_string):
|
||||
"""Convert a raw config into a form that preserves comments etc."""
|
||||
results = ['[__COMMENTS__]']
|
||||
counter = itertools.count(0)
|
||||
|
||||
section_re = re.compile(r'^(\[[^\]]+\])\s*(.+)$')
|
||||
blank_line_re = re.compile(r'^\s*$')
|
||||
comment_re = re.compile(r'^(#|;)')
|
||||
inline_comment_re = re.compile(r' ;')
|
||||
|
||||
def newlines(match):
|
||||
return '__BLANK%d__ =' % next(counter)
|
||||
|
||||
def comments(match):
|
||||
if match.group(1) == '#':
|
||||
return '__HASH%d__ =' % next(counter)
|
||||
elif match.group(1) == ';':
|
||||
return '__SEMICOLON%d__ =' % next(counter)
|
||||
|
||||
def inlinecomments(match):
|
||||
return '\n__INLINE%d__ =' % next(counter)
|
||||
|
||||
def sections(match):
|
||||
return '%s\n__SECTION%d__ = %s' % (
|
||||
match.group(1), next(counter), match.group(2))
|
||||
|
||||
for line in config_string.splitlines():
|
||||
line = blank_line_re.sub(newlines, line)
|
||||
line = section_re.sub(sections, line)
|
||||
line = comment_re.sub(comments, line)
|
||||
line = inline_comment_re.sub(inlinecomments, line)
|
||||
results.append(line)
|
||||
return '\n'.join(results)
|
||||
|
||||
|
||||
def _postprocess(config_string):
|
||||
"""Converts a preprocessed config back to original form."""
|
||||
flags = re.IGNORECASE | re.MULTILINE
|
||||
result = re.sub(r'^\[__COMMENTS__\](\n|$)', '', config_string, flags=flags)
|
||||
result = re.sub(r'\n__INLINE\d+__ =(.*)$', ' ;\g<1>', result, flags=flags)
|
||||
result = re.sub(r'^__HASH\d+__ =(.*)$', '#\g<1>', result, flags=flags)
|
||||
result = re.sub(r'^__SEMICOLON\d+__ =(.*)$', ';\g<1>', result, flags=flags)
|
||||
result = re.sub(r'\n__SECTION\d+__ =(.*)$', '\g<1>', result, flags=flags)
|
||||
result = re.sub(r'^__BLANK\d+__ =$', '', result, flags=flags)
|
||||
return result
|
||||
|
||||
|
||||
class Proxy(collections.Mapping):
|
||||
def __init__(self, data):
|
||||
self._data = data
|
||||
|
||||
@ -5,13 +5,13 @@ import itertools
|
||||
|
||||
from mopidy.models import Track
|
||||
from mopidy.frontends.mpd import translator
|
||||
from mopidy.frontends.mpd.exceptions import MpdNotImplemented
|
||||
from mopidy.frontends.mpd.exceptions import MpdArgError, MpdNotImplemented
|
||||
from mopidy.frontends.mpd.protocol import handle_request, stored_playlists
|
||||
|
||||
|
||||
QUERY_RE = (
|
||||
r'(?P<mpd_query>("?([Aa]lbum|[Aa]rtist|[Aa]lbumartist|[Dd]ate|[Ff]ile|'
|
||||
r'[Ff]ilename|[Tt]itle|[Aa]ny)"? "[^"]*"\s?)+)$')
|
||||
r'[Ff]ilename|[Tt]itle|[Tt]rack|[Aa]ny)"? "[^"]*"\s?)+)$')
|
||||
|
||||
|
||||
def _get_field(field, search_results):
|
||||
@ -54,7 +54,16 @@ def count(context, mpd_query):
|
||||
- does not add quotes around the tag argument.
|
||||
- use multiple tag-needle pairs to make more specific searches.
|
||||
"""
|
||||
return [('songs', 0), ('playtime', 0)] # TODO
|
||||
try:
|
||||
query = translator.query_from_mpd_search_format(mpd_query)
|
||||
except ValueError:
|
||||
raise MpdArgError('incorrect arguments', command='count')
|
||||
results = context.core.library.find_exact(**query).get()
|
||||
result_tracks = _get_tracks(results)
|
||||
return [
|
||||
('songs', len(result_tracks)),
|
||||
('playtime', sum(track.length for track in result_tracks) / 1000),
|
||||
]
|
||||
|
||||
|
||||
@handle_request(r'^find ' + QUERY_RE)
|
||||
|
||||
@ -193,6 +193,7 @@ MPD_SEARCH_QUERY_RE = re.compile(r"""
|
||||
| [Ff]ile
|
||||
| [Ff]ilename
|
||||
| [Tt]itle
|
||||
| [Tt]rack
|
||||
| [Aa]ny
|
||||
)
|
||||
"? # End of optional quote around the field type
|
||||
@ -211,6 +212,7 @@ MPD_SEARCH_QUERY_PART_RE = re.compile(r"""
|
||||
| [Ff]ile
|
||||
| [Ff]ilename
|
||||
| [Tt]itle
|
||||
| [Tt]rack
|
||||
| [Aa]ny
|
||||
))
|
||||
"? # End of optional quote around the field type
|
||||
@ -234,6 +236,8 @@ def query_from_mpd_search_format(mpd_query):
|
||||
field = m.groupdict()['field'].lower()
|
||||
if field == 'title':
|
||||
field = 'track'
|
||||
elif field == 'track':
|
||||
field = 'track_no'
|
||||
elif field in ('file', 'filename'):
|
||||
field = 'uri'
|
||||
what = m.groupdict()['what']
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
import pykka
|
||||
@ -11,6 +12,8 @@ from mopidy.models import Track, Album, Artist
|
||||
from tests import path_to_data_dir
|
||||
|
||||
|
||||
# TODO: update tests to only use backend, not core. we need a seperate
|
||||
# core test that does this integration test.
|
||||
class LocalLibraryProviderTest(unittest.TestCase):
|
||||
artists = [
|
||||
Artist(name='artist1'),
|
||||
@ -29,15 +32,15 @@ class LocalLibraryProviderTest(unittest.TestCase):
|
||||
Track(
|
||||
uri='local:track:path1', name='track1',
|
||||
artists=[artists[0]], album=albums[0],
|
||||
date='2001-02-03', length=4000),
|
||||
date='2001-02-03', length=4000, track_no=1),
|
||||
Track(
|
||||
uri='local:track:path2', name='track2',
|
||||
artists=[artists[1]], album=albums[1],
|
||||
date='2002', length=4000),
|
||||
date='2002', length=4000, track_no=2),
|
||||
Track(
|
||||
uri='local:track:path3', name='track3',
|
||||
artists=[artists[3]], album=albums[2],
|
||||
date='2003', length=4000),
|
||||
date='2003', length=4000, track_no=3),
|
||||
]
|
||||
|
||||
config = {
|
||||
@ -49,7 +52,6 @@ class LocalLibraryProviderTest(unittest.TestCase):
|
||||
}
|
||||
|
||||
def setUp(self):
|
||||
|
||||
self.backend = actor.LocalBackend.start(
|
||||
config=self.config, audio=None).proxy()
|
||||
self.core = core.Core(backends=[self.backend])
|
||||
@ -65,9 +67,31 @@ class LocalLibraryProviderTest(unittest.TestCase):
|
||||
def test_refresh_uri(self):
|
||||
pass
|
||||
|
||||
@unittest.SkipTest
|
||||
def test_refresh_missing_uri(self):
|
||||
pass
|
||||
# Verifies that https://github.com/mopidy/mopidy/issues/500
|
||||
# has been fixed.
|
||||
|
||||
tag_cache = tempfile.NamedTemporaryFile()
|
||||
with open(self.config['local']['tag_cache_file']) as fh:
|
||||
tag_cache.write(fh.read())
|
||||
tag_cache.flush()
|
||||
|
||||
config = {'local': self.config['local'].copy()}
|
||||
config['local']['tag_cache_file'] = tag_cache.name
|
||||
backend = actor.LocalBackend(config=config, audio=None)
|
||||
|
||||
# Sanity check that value is in tag cache
|
||||
result = backend.library.lookup(self.tracks[0].uri)
|
||||
self.assertEqual(result, self.tracks[0:1])
|
||||
|
||||
# Clear tag cache and refresh
|
||||
tag_cache.seek(0)
|
||||
tag_cache.truncate()
|
||||
backend.library.refresh()
|
||||
|
||||
# Now it should be gone.
|
||||
result = backend.library.lookup(self.tracks[0].uri)
|
||||
self.assertEqual(result, [])
|
||||
|
||||
def test_lookup(self):
|
||||
tracks = self.library.lookup(self.tracks[0].uri)
|
||||
@ -87,6 +111,18 @@ class LocalLibraryProviderTest(unittest.TestCase):
|
||||
result = self.library.find_exact(album=['unknown artist'])
|
||||
self.assertEqual(list(result[0].tracks), [])
|
||||
|
||||
result = self.library.find_exact(date=['1990'])
|
||||
self.assertEqual(list(result[0].tracks), [])
|
||||
|
||||
result = self.library.find_exact(track_no=[9])
|
||||
self.assertEqual(list(result[0].tracks), [])
|
||||
|
||||
result = self.library.find_exact(uri=['fake uri'])
|
||||
self.assertEqual(list(result[0].tracks), [])
|
||||
|
||||
result = self.library.find_exact(any=['unknown any'])
|
||||
self.assertEqual(list(result[0].tracks), [])
|
||||
|
||||
def test_find_exact_uri(self):
|
||||
track_1_uri = 'local:track:path1'
|
||||
result = self.library.find_exact(uri=track_1_uri)
|
||||
@ -130,6 +166,13 @@ class LocalLibraryProviderTest(unittest.TestCase):
|
||||
result = self.library.find_exact(albumartist=['artist3'])
|
||||
self.assertEqual(list(result[0].tracks), [self.tracks[2]])
|
||||
|
||||
def test_find_exact_track_no(self):
|
||||
result = self.library.find_exact(track_no=[1])
|
||||
self.assertEqual(list(result[0].tracks), self.tracks[:1])
|
||||
|
||||
result = self.library.find_exact(track_no=[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(list(result[0].tracks), [])
|
||||
@ -145,10 +188,16 @@ class LocalLibraryProviderTest(unittest.TestCase):
|
||||
result = self.library.find_exact(any=['artist1'])
|
||||
self.assertEqual(list(result[0].tracks), self.tracks[:1])
|
||||
|
||||
result = self.library.find_exact(any=['artist2'])
|
||||
self.assertEqual(list(result[0].tracks), self.tracks[1:2])
|
||||
|
||||
# Matches on track
|
||||
result = self.library.find_exact(any=['track1'])
|
||||
self.assertEqual(list(result[0].tracks), self.tracks[:1])
|
||||
|
||||
result = self.library.find_exact(any=['track2'])
|
||||
self.assertEqual(list(result[0].tracks), self.tracks[1:2])
|
||||
|
||||
# Matches on track album
|
||||
result = self.library.find_exact(any=['album1'])
|
||||
self.assertEqual(list(result[0].tracks), self.tracks[:1])
|
||||
@ -157,6 +206,10 @@ class LocalLibraryProviderTest(unittest.TestCase):
|
||||
result = self.library.find_exact(any=['artist3'])
|
||||
self.assertEqual(list(result[0].tracks), self.tracks[2:3])
|
||||
|
||||
# Matches on track year
|
||||
result = self.library.find_exact(any=['2002'])
|
||||
self.assertEqual(list(result[0].tracks), self.tracks[1:2])
|
||||
|
||||
# Matches on URI
|
||||
result = self.library.find_exact(any=['local:track:path1'])
|
||||
self.assertEqual(list(result[0].tracks), self.tracks[:1])
|
||||
@ -175,6 +228,15 @@ class LocalLibraryProviderTest(unittest.TestCase):
|
||||
test = lambda: self.library.find_exact(album=[''])
|
||||
self.assertRaises(LookupError, test)
|
||||
|
||||
test = lambda: self.library.find_exact(track_no=[])
|
||||
self.assertRaises(LookupError, test)
|
||||
|
||||
test = lambda: self.library.find_exact(date=[''])
|
||||
self.assertRaises(LookupError, test)
|
||||
|
||||
test = lambda: self.library.find_exact(any=[''])
|
||||
self.assertRaises(LookupError, test)
|
||||
|
||||
def test_search_no_hits(self):
|
||||
result = self.library.search(track=['unknown track'])
|
||||
self.assertEqual(list(result[0].tracks), [])
|
||||
@ -185,10 +247,16 @@ class LocalLibraryProviderTest(unittest.TestCase):
|
||||
result = self.library.search(album=['unknown artist'])
|
||||
self.assertEqual(list(result[0].tracks), [])
|
||||
|
||||
result = self.library.search(uri=['unknown'])
|
||||
result = self.library.search(track_no=[9])
|
||||
self.assertEqual(list(result[0].tracks), [])
|
||||
|
||||
result = self.library.search(any=['unknown'])
|
||||
result = self.library.search(date=['unknown date'])
|
||||
self.assertEqual(list(result[0].tracks), [])
|
||||
|
||||
result = self.library.search(uri=['unknown uri'])
|
||||
self.assertEqual(list(result[0].tracks), [])
|
||||
|
||||
result = self.library.search(any=['unknown anything'])
|
||||
self.assertEqual(list(result[0].tracks), [])
|
||||
|
||||
def test_search_uri(self):
|
||||
@ -245,6 +313,13 @@ class LocalLibraryProviderTest(unittest.TestCase):
|
||||
result = self.library.search(date=['2002'])
|
||||
self.assertEqual(list(result[0].tracks), self.tracks[1:2])
|
||||
|
||||
def test_search_track_no(self):
|
||||
result = self.library.search(track_no=[1])
|
||||
self.assertEqual(list(result[0].tracks), self.tracks[:1])
|
||||
|
||||
result = self.library.search(track_no=[2])
|
||||
self.assertEqual(list(result[0].tracks), self.tracks[1:2])
|
||||
|
||||
def test_search_any(self):
|
||||
# Matches on track artist
|
||||
result = self.library.search(any=['Tist1'])
|
||||
@ -254,6 +329,9 @@ class LocalLibraryProviderTest(unittest.TestCase):
|
||||
result = self.library.search(any=['Rack1'])
|
||||
self.assertEqual(list(result[0].tracks), self.tracks[:1])
|
||||
|
||||
result = self.library.search(any=['Rack2'])
|
||||
self.assertEqual(list(result[0].tracks), self.tracks[1:2])
|
||||
|
||||
# Matches on track album
|
||||
result = self.library.search(any=['Bum1'])
|
||||
self.assertEqual(list(result[0].tracks), self.tracks[:1])
|
||||
@ -280,6 +358,9 @@ class LocalLibraryProviderTest(unittest.TestCase):
|
||||
test = lambda: self.library.search(album=[''])
|
||||
self.assertRaises(LookupError, test)
|
||||
|
||||
test = lambda: self.library.search(date=[''])
|
||||
self.assertRaises(LookupError, test)
|
||||
|
||||
test = lambda: self.library.search(uri=[''])
|
||||
self.assertRaises(LookupError, test)
|
||||
|
||||
|
||||
@ -106,3 +106,162 @@ class ValidateTest(unittest.TestCase):
|
||||
self.assertEqual({'foo': {'bar': 'bad'}}, errors)
|
||||
|
||||
# TODO: add more tests
|
||||
|
||||
|
||||
INPUT_CONFIG = """# comments before first section should work
|
||||
|
||||
[section] anything goes ; after the [] block it seems.
|
||||
; this is a valid comment
|
||||
this-should-equal-baz = baz ; as this is a comment
|
||||
this-should-equal-everything = baz # as this is not a comment
|
||||
|
||||
# this is also a comment ; and the next line should be a blank comment.
|
||||
;
|
||||
# foo # = should all be treated as a comment."""
|
||||
|
||||
PROCESSED_CONFIG = """[__COMMENTS__]
|
||||
__HASH0__ = comments before first section should work
|
||||
__BLANK1__ =
|
||||
[section]
|
||||
__SECTION2__ = anything goes
|
||||
__INLINE3__ = after the [] block it seems.
|
||||
__SEMICOLON4__ = this is a valid comment
|
||||
this-should-equal-baz = baz
|
||||
__INLINE5__ = as this is a comment
|
||||
this-should-equal-everything = baz # as this is not a comment
|
||||
__BLANK6__ =
|
||||
__HASH7__ = this is also a comment
|
||||
__INLINE8__ = and the next line should be a blank comment.
|
||||
__SEMICOLON9__ =
|
||||
__HASH10__ = foo # = should all be treated as a comment."""
|
||||
|
||||
|
||||
class PreProcessorTest(unittest.TestCase):
|
||||
maxDiff = None # Show entire diff.
|
||||
|
||||
def test_empty_config(self):
|
||||
result = config._preprocess('')
|
||||
self.assertEqual(result, '[__COMMENTS__]')
|
||||
|
||||
def test_plain_section(self):
|
||||
result = config._preprocess('[section]\nfoo = bar')
|
||||
self.assertEqual(result, '[__COMMENTS__]\n'
|
||||
'[section]\n'
|
||||
'foo = bar')
|
||||
|
||||
def test_initial_comments(self):
|
||||
result = config._preprocess('; foobar')
|
||||
self.assertEqual(result, '[__COMMENTS__]\n'
|
||||
'__SEMICOLON0__ = foobar')
|
||||
|
||||
result = config._preprocess('# foobar')
|
||||
self.assertEqual(result, '[__COMMENTS__]\n'
|
||||
'__HASH0__ = foobar')
|
||||
|
||||
result = config._preprocess('; foo\n# bar')
|
||||
self.assertEqual(result, '[__COMMENTS__]\n'
|
||||
'__SEMICOLON0__ = foo\n'
|
||||
'__HASH1__ = bar')
|
||||
|
||||
def test_initial_comment_inline_handling(self):
|
||||
result = config._preprocess('; foo ; bar ; baz')
|
||||
self.assertEqual(result, '[__COMMENTS__]\n'
|
||||
'__SEMICOLON0__ = foo\n'
|
||||
'__INLINE1__ = bar\n'
|
||||
'__INLINE2__ = baz')
|
||||
|
||||
def test_inline_semicolon_comment(self):
|
||||
result = config._preprocess('[section]\nfoo = bar ; baz')
|
||||
self.assertEqual(result, '[__COMMENTS__]\n'
|
||||
'[section]\n'
|
||||
'foo = bar\n'
|
||||
'__INLINE0__ = baz')
|
||||
|
||||
def test_no_inline_hash_comment(self):
|
||||
result = config._preprocess('[section]\nfoo = bar # baz')
|
||||
self.assertEqual(result, '[__COMMENTS__]\n'
|
||||
'[section]\n'
|
||||
'foo = bar # baz')
|
||||
|
||||
def test_section_extra_text(self):
|
||||
result = config._preprocess('[section] foobar')
|
||||
self.assertEqual(result, '[__COMMENTS__]\n'
|
||||
'[section]\n'
|
||||
'__SECTION0__ = foobar')
|
||||
|
||||
def test_section_extra_text_inline_semicolon(self):
|
||||
result = config._preprocess('[section] foobar ; baz')
|
||||
self.assertEqual(result, '[__COMMENTS__]\n'
|
||||
'[section]\n'
|
||||
'__SECTION0__ = foobar\n'
|
||||
'__INLINE1__ = baz')
|
||||
|
||||
def test_conversion(self):
|
||||
"""Tests all of the above cases at once."""
|
||||
result = config._preprocess(INPUT_CONFIG)
|
||||
self.assertEqual(result, PROCESSED_CONFIG)
|
||||
|
||||
|
||||
class PostProcessorTest(unittest.TestCase):
|
||||
maxDiff = None # Show entire diff.
|
||||
|
||||
def test_empty_config(self):
|
||||
result = config._postprocess('[__COMMENTS__]')
|
||||
self.assertEqual(result, '')
|
||||
|
||||
def test_plain_section(self):
|
||||
result = config._postprocess('[__COMMENTS__]\n'
|
||||
'[section]\n'
|
||||
'foo = bar')
|
||||
self.assertEqual(result, '[section]\nfoo = bar')
|
||||
|
||||
def test_initial_comments(self):
|
||||
result = config._postprocess('[__COMMENTS__]\n'
|
||||
'__SEMICOLON0__ = foobar')
|
||||
self.assertEqual(result, '; foobar')
|
||||
|
||||
result = config._postprocess('[__COMMENTS__]\n'
|
||||
'__HASH0__ = foobar')
|
||||
self.assertEqual(result, '# foobar')
|
||||
|
||||
result = config._postprocess('[__COMMENTS__]\n'
|
||||
'__SEMICOLON0__ = foo\n'
|
||||
'__HASH1__ = bar')
|
||||
self.assertEqual(result, '; foo\n# bar')
|
||||
|
||||
def test_initial_comment_inline_handling(self):
|
||||
result = config._postprocess('[__COMMENTS__]\n'
|
||||
'__SEMICOLON0__ = foo\n'
|
||||
'__INLINE1__ = bar\n'
|
||||
'__INLINE2__ = baz')
|
||||
self.assertEqual(result, '; foo ; bar ; baz')
|
||||
|
||||
def test_inline_semicolon_comment(self):
|
||||
result = config._postprocess('[__COMMENTS__]\n'
|
||||
'[section]\n'
|
||||
'foo = bar\n'
|
||||
'__INLINE0__ = baz')
|
||||
self.assertEqual(result, '[section]\nfoo = bar ; baz')
|
||||
|
||||
def test_no_inline_hash_comment(self):
|
||||
result = config._preprocess('[section]\nfoo = bar # baz')
|
||||
self.assertEqual(result, '[__COMMENTS__]\n'
|
||||
'[section]\n'
|
||||
'foo = bar # baz')
|
||||
|
||||
def test_section_extra_text(self):
|
||||
result = config._postprocess('[__COMMENTS__]\n'
|
||||
'[section]\n'
|
||||
'__SECTION0__ = foobar')
|
||||
self.assertEqual(result, '[section] foobar')
|
||||
|
||||
def test_section_extra_text_inline_semicolon(self):
|
||||
result = config._postprocess('[__COMMENTS__]\n'
|
||||
'[section]\n'
|
||||
'__SECTION0__ = foobar\n'
|
||||
'__INLINE1__ = baz')
|
||||
self.assertEqual(result, '[section] foobar ; baz')
|
||||
|
||||
def test_conversion(self):
|
||||
result = config._postprocess(PROCESSED_CONFIG)
|
||||
self.assertEqual(result, INPUT_CONFIG)
|
||||
|
||||
@ -9,6 +9,7 @@ Artist: artist1
|
||||
Title: track1
|
||||
Album: album1
|
||||
Date: 2001-02-03
|
||||
Track: 1
|
||||
Time: 4
|
||||
key: key2
|
||||
file: /path2
|
||||
@ -16,6 +17,7 @@ Artist: artist2
|
||||
Title: track2
|
||||
Album: album2
|
||||
Date: 2002
|
||||
Track: 2
|
||||
Time: 4
|
||||
key: key3
|
||||
file: /path3
|
||||
@ -24,5 +26,6 @@ AlbumArtist: artist3
|
||||
Title: track3
|
||||
Album: album3
|
||||
Date: 2003
|
||||
Track: 3
|
||||
Time: 4
|
||||
songList end
|
||||
|
||||
@ -24,6 +24,28 @@ class MusicDatabaseHandlerTest(protocol.BaseTestCase):
|
||||
self.assertInResponse('playtime: 0')
|
||||
self.assertInResponse('OK')
|
||||
|
||||
def test_count_correct_length(self):
|
||||
# Count the lone track
|
||||
self.backend.library.dummy_find_exact_result = SearchResult(
|
||||
tracks=[
|
||||
Track(uri='dummy:a', name="foo", date="2001", length=4000),
|
||||
])
|
||||
self.sendRequest('count "title" "foo"')
|
||||
self.assertInResponse('songs: 1')
|
||||
self.assertInResponse('playtime: 4')
|
||||
self.assertInResponse('OK')
|
||||
|
||||
# Count multiple tracks
|
||||
self.backend.library.dummy_find_exact_result = SearchResult(
|
||||
tracks=[
|
||||
Track(uri='dummy:b', date="2001", length=50000),
|
||||
Track(uri='dummy:c', date="2001", length=600000),
|
||||
])
|
||||
self.sendRequest('count "date" "2001"')
|
||||
self.assertInResponse('songs: 2')
|
||||
self.assertInResponse('playtime: 650')
|
||||
self.assertInResponse('OK')
|
||||
|
||||
def test_findadd(self):
|
||||
self.backend.library.dummy_find_exact_result = SearchResult(
|
||||
tracks=[Track(uri='dummy:a', name='A')])
|
||||
@ -263,6 +285,18 @@ class MusicDatabaseFindTest(protocol.BaseTestCase):
|
||||
self.sendRequest('find title "what"')
|
||||
self.assertInResponse('OK')
|
||||
|
||||
def test_find_track_no(self):
|
||||
self.sendRequest('find "track" "10"')
|
||||
self.assertInResponse('OK')
|
||||
|
||||
def test_find_track_no_without_quotes(self):
|
||||
self.sendRequest('find track "10"')
|
||||
self.assertInResponse('OK')
|
||||
|
||||
def test_find_track_no_without_filter_value(self):
|
||||
self.sendRequest('find "track" ""')
|
||||
self.assertInResponse('OK')
|
||||
|
||||
def test_find_date(self):
|
||||
self.sendRequest('find "date" "2002-01-01"')
|
||||
self.assertInResponse('OK')
|
||||
@ -633,6 +667,18 @@ class MusicDatabaseSearchTest(protocol.BaseTestCase):
|
||||
self.sendRequest('search "any" ""')
|
||||
self.assertInResponse('OK')
|
||||
|
||||
def test_search_track_no(self):
|
||||
self.sendRequest('search "track" "10"')
|
||||
self.assertInResponse('OK')
|
||||
|
||||
def test_search_track_no_without_quotes(self):
|
||||
self.sendRequest('search track "10"')
|
||||
self.assertInResponse('OK')
|
||||
|
||||
def test_search_track_no_without_filter_value(self):
|
||||
self.sendRequest('search "track" ""')
|
||||
self.assertInResponse('OK')
|
||||
|
||||
def test_search_date(self):
|
||||
self.sendRequest('search "date" "2002-01-01"')
|
||||
self.assertInResponse('OK')
|
||||
|
||||
Loading…
Reference in New Issue
Block a user