From f4d971d3a843482e72fd80c10e7ec7eb50fb7dca Mon Sep 17 00:00:00 2001 From: Tom Jaspers Date: Fri, 6 Feb 2015 10:36:23 +0100 Subject: [PATCH 1/4] PathQueryTest now also tests Album queries --- test/test_query.py | 55 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 51 insertions(+), 4 deletions(-) diff --git a/test/test_query.py b/test/test_query.py index c76ddf11b..55980d0e1 100644 --- a/test/test_query.py +++ b/test/test_query.py @@ -60,8 +60,12 @@ class AnyFieldQueryTest(_common.LibTestCase): class AssertsMixin(object): def assert_matched(self, results, titles): + # TODO: refactor to "assert_items_matched" for clarity self.assertEqual([i.title for i in results], titles) + def assert_albums_matched(self, results, albums): + self.assertEqual([a.album for a in results], albums) + # A test case class providing a library with some dummy data and some # assertions involving that data. @@ -343,76 +347,119 @@ class PathQueryTest(_common.LibTestCase, TestHelper, AssertsMixin): super(PathQueryTest, self).setUp() self.i.path = '/a/b/c.mp3' self.i.title = 'path item' + self.i.album = 'path album' self.i.store() + self.lib.add_album([self.i]) def test_path_exact_match(self): q = 'path:/a/b/c.mp3' results = self.lib.items(q) self.assert_matched(results, ['path item']) + results = self.lib.albums(q) + self.assert_albums_matched(results, []) + def test_parent_directory_no_slash(self): q = 'path:/a' results = self.lib.items(q) self.assert_matched(results, ['path item']) + results = self.lib.albums(q) + self.assert_albums_matched(results, ['path album']) + def test_parent_directory_with_slash(self): q = 'path:/a/' results = self.lib.items(q) self.assert_matched(results, ['path item']) + results = self.lib.albums(q) + self.assert_albums_matched(results, ['path album']) + def test_no_match(self): q = 'path:/xyzzy/' results = self.lib.items(q) self.assert_matched(results, []) + results = self.lib.albums(q) + self.assert_albums_matched(results, []) + def test_fragment_no_match(self): q = 'path:/b/' results = self.lib.items(q) self.assert_matched(results, []) + results = self.lib.albums(q) + self.assert_albums_matched(results, []) + def test_nonnorm_path(self): q = 'path:/x/../a/b' results = self.lib.items(q) self.assert_matched(results, ['path item']) + results = self.lib.albums(q) + self.assert_albums_matched(results, ['path album']) + def test_slashed_query_matches_path(self): q = '/a/b' results = self.lib.items(q) self.assert_matched(results, ['path item']) + results = self.lib.albums(q) + self.assert_albums_matched(results, ['path album']) + def test_non_slashed_does_not_match_path(self): q = 'c.mp3' results = self.lib.items(q) self.assert_matched(results, []) + results = self.lib.albums(q) + self.assert_albums_matched(results, []) + def test_slashes_in_explicit_field_does_not_match_path(self): q = 'title:/a/b' results = self.lib.items(q) self.assert_matched(results, []) - def test_path_regex(self): + def test_path_item_regex(self): q = 'path::\\.mp3$' results = self.lib.items(q) self.assert_matched(results, ['path item']) + def test_path_album_regex(self): + q = 'path::b' + results = self.lib.albums(q) + self.assert_albums_matched(results, ['path album']) + def test_escape_underscore(self): - self.add_item(path='/a/_/title.mp3', title='with underscore') + self.add_album(path='/a/_/title.mp3', title='with underscore', + album='album with underscore') q = 'path:/a/_' results = self.lib.items(q) self.assert_matched(results, ['with underscore']) + results = self.lib.albums(q) + self.assert_albums_matched(results, ['album with underscore']) + def test_escape_percent(self): - self.add_item(path='/a/%/title.mp3', title='with percent') + self.add_album(path='/a/%/title.mp3', title='with percent', + album='album with percent') q = 'path:/a/%' results = self.lib.items(q) self.assert_matched(results, ['with percent']) + results = self.lib.albums(q) + self.assert_albums_matched(results, ['album with percent']) + def test_escape_backslash(self): - self.add_item(path=r'/a/\x/title.mp3', title='with backslash') + self.add_album(path=r'/a/\x/title.mp3', title='with backslash', + album='album with backslash') q = r'path:/a/\\x' results = self.lib.items(q) self.assert_matched(results, ['with backslash']) + results = self.lib.albums(q) + self.assert_albums_matched(results, ['album with backslash']) + class IntQueryTest(unittest.TestCase, TestHelper): From 049aa2f297ba4a3ee6a870f6a8474caa5a8938ef Mon Sep 17 00:00:00 2001 From: Tom Jaspers Date: Fri, 6 Feb 2015 10:38:41 +0100 Subject: [PATCH 2/4] Album path queries get constructed automatically Ensures that relative path queries also work for -a mode --- beets/library.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/beets/library.py b/beets/library.py index e4fbd1d49..40051c354 100644 --- a/beets/library.py +++ b/beets/library.py @@ -794,6 +794,10 @@ class Album(LibModel): _search_fields = ('album', 'albumartist', 'genre') + _types = { + 'path': PathType(), + } + _sorts = { 'albumartist': SmartArtistSort, 'artist': SmartArtistSort, From 3ec44aab3ecf6803f09702f1080e015aa7cb0a00 Mon Sep 17 00:00:00 2001 From: Tom Jaspers Date: Fri, 6 Feb 2015 10:42:46 +0100 Subject: [PATCH 3/4] Update `_types` field instead of assigning it Assigning it would override our pre-defined types (such as `path` in Album._types) --- beets/ui/__init__.py | 4 ++-- test/helper.py | 11 +++++++---- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/beets/ui/__init__.py b/beets/ui/__init__.py index e09043cbb..02a7a9478 100644 --- a/beets/ui/__init__.py +++ b/beets/ui/__init__.py @@ -839,8 +839,8 @@ def _setup(options, lib=None): if lib is None: lib = _open_library(config) plugins.send("library_opened", lib=lib) - library.Item._types = plugins.types(library.Item) - library.Album._types = plugins.types(library.Album) + library.Item._types.update(plugins.types(library.Item)) + library.Album._types.update(plugins.types(library.Album)) return subcommands, plugins, lib diff --git a/test/helper.py b/test/helper.py index 64db9e8b1..e929eecff 100644 --- a/test/helper.py +++ b/test/helper.py @@ -199,8 +199,11 @@ class TestHelper(object): beets.config['plugins'] = plugins beets.plugins.load_plugins(plugins) beets.plugins.find_plugins() - Item._types = beets.plugins.types(Item) - Album._types = beets.plugins.types(Album) + # Take a backup of the original _types to restore when unloading + Item._original_types = dict(Item._types) + Album._original_types = dict(Album._types) + Item._types.update(beets.plugins.types(Item)) + Album._types.update(beets.plugins.types(Album)) def unload_plugins(self): """Unload all plugins and remove the from the configuration. @@ -209,8 +212,8 @@ class TestHelper(object): beets.config['plugins'] = [] beets.plugins._classes = set() beets.plugins._instances = {} - Item._types = {} - Album._types = {} + Item._types = Item._original_types + Album._types = Album._original_types def create_importer(self, item_count=1, album_count=1): """Create files to import and return corresponding session. From 0a81aae142396569b1b20d03e76d6dde3ce4e438 Mon Sep 17 00:00:00 2001 From: Tom Jaspers Date: Sun, 8 Feb 2015 16:34:28 +0100 Subject: [PATCH 4/4] PathQueryTest: rename assert_matched for items --- test/test_query.py | 91 +++++++++++++++++++++++----------------------- 1 file changed, 45 insertions(+), 46 deletions(-) diff --git a/test/test_query.py b/test/test_query.py index 55980d0e1..55450d0ad 100644 --- a/test/test_query.py +++ b/test/test_query.py @@ -59,8 +59,7 @@ class AnyFieldQueryTest(_common.LibTestCase): class AssertsMixin(object): - def assert_matched(self, results, titles): - # TODO: refactor to "assert_items_matched" for clarity + def assert_items_matched(self, results, titles): self.assertEqual([i.title for i in results], titles) def assert_albums_matched(self, results, albums): @@ -93,8 +92,8 @@ class DummyDataTestCase(_common.TestCase, AssertsMixin): self.lib.add(item) self.lib.add_album(items[:2]) - def assert_matched_all(self, results): - self.assert_matched(results, [ + def assert_items_matched_all(self, results): + self.assert_items_matched(results, [ 'foo bar', 'baz qux', 'beets 4 eva', @@ -105,72 +104,72 @@ class GetTest(DummyDataTestCase): def test_get_empty(self): q = '' results = self.lib.items(q) - self.assert_matched_all(results) + self.assert_items_matched_all(results) def test_get_none(self): q = None results = self.lib.items(q) - self.assert_matched_all(results) + self.assert_items_matched_all(results) def test_get_one_keyed_term(self): q = 'title:qux' results = self.lib.items(q) - self.assert_matched(results, ['baz qux']) + self.assert_items_matched(results, ['baz qux']) def test_get_one_keyed_regexp(self): q = r'artist::t.+r' results = self.lib.items(q) - self.assert_matched(results, ['beets 4 eva']) + self.assert_items_matched(results, ['beets 4 eva']) def test_get_one_unkeyed_term(self): q = 'three' results = self.lib.items(q) - self.assert_matched(results, ['beets 4 eva']) + self.assert_items_matched(results, ['beets 4 eva']) def test_get_one_unkeyed_regexp(self): q = r':x$' results = self.lib.items(q) - self.assert_matched(results, ['baz qux']) + self.assert_items_matched(results, ['baz qux']) def test_get_no_matches(self): q = 'popebear' results = self.lib.items(q) - self.assert_matched(results, []) + self.assert_items_matched(results, []) def test_invalid_key(self): q = 'pope:bear' results = self.lib.items(q) # Matches nothing since the flexattr is not present on the # objects. - self.assert_matched(results, []) + self.assert_items_matched(results, []) def test_term_case_insensitive(self): q = 'oNE' results = self.lib.items(q) - self.assert_matched(results, ['foo bar']) + self.assert_items_matched(results, ['foo bar']) def test_regexp_case_sensitive(self): q = r':oNE' results = self.lib.items(q) - self.assert_matched(results, []) + self.assert_items_matched(results, []) q = r':one' results = self.lib.items(q) - self.assert_matched(results, ['foo bar']) + self.assert_items_matched(results, ['foo bar']) def test_term_case_insensitive_with_key(self): q = 'artist:thrEE' results = self.lib.items(q) - self.assert_matched(results, ['beets 4 eva']) + self.assert_items_matched(results, ['beets 4 eva']) def test_key_case_insensitive(self): q = 'ArTiST:three' results = self.lib.items(q) - self.assert_matched(results, ['beets 4 eva']) + self.assert_items_matched(results, ['beets 4 eva']) def test_unkeyed_term_matches_multiple_columns(self): q = 'baz' results = self.lib.items(q) - self.assert_matched(results, [ + self.assert_items_matched(results, [ 'foo bar', 'baz qux', ]) @@ -178,7 +177,7 @@ class GetTest(DummyDataTestCase): def test_unkeyed_regexp_matches_multiple_columns(self): q = r':z$' results = self.lib.items(q) - self.assert_matched(results, [ + self.assert_items_matched(results, [ 'foo bar', 'baz qux', ]) @@ -186,41 +185,41 @@ class GetTest(DummyDataTestCase): def test_keyed_term_matches_only_one_column(self): q = 'title:baz' results = self.lib.items(q) - self.assert_matched(results, ['baz qux']) + self.assert_items_matched(results, ['baz qux']) def test_keyed_regexp_matches_only_one_column(self): q = r'title::baz' results = self.lib.items(q) - self.assert_matched(results, [ + self.assert_items_matched(results, [ 'baz qux', ]) def test_multiple_terms_narrow_search(self): q = 'qux baz' results = self.lib.items(q) - self.assert_matched(results, [ + self.assert_items_matched(results, [ 'baz qux', ]) def test_multiple_regexps_narrow_search(self): q = r':baz :qux' results = self.lib.items(q) - self.assert_matched(results, ['baz qux']) + self.assert_items_matched(results, ['baz qux']) def test_mixed_terms_regexps_narrow_search(self): q = r':baz qux' results = self.lib.items(q) - self.assert_matched(results, ['baz qux']) + self.assert_items_matched(results, ['baz qux']) def test_single_year(self): q = 'year:2001' results = self.lib.items(q) - self.assert_matched(results, ['foo bar']) + self.assert_items_matched(results, ['foo bar']) def test_year_range(self): q = 'year:2000..2002' results = self.lib.items(q) - self.assert_matched(results, [ + self.assert_items_matched(results, [ 'foo bar', 'baz qux', ]) @@ -228,22 +227,22 @@ class GetTest(DummyDataTestCase): def test_singleton_true(self): q = 'singleton:true' results = self.lib.items(q) - self.assert_matched(results, ['beets 4 eva']) + self.assert_items_matched(results, ['beets 4 eva']) def test_singleton_false(self): q = 'singleton:false' results = self.lib.items(q) - self.assert_matched(results, ['foo bar', 'baz qux']) + self.assert_items_matched(results, ['foo bar', 'baz qux']) def test_compilation_true(self): q = 'comp:true' results = self.lib.items(q) - self.assert_matched(results, ['foo bar', 'baz qux']) + self.assert_items_matched(results, ['foo bar', 'baz qux']) def test_compilation_false(self): q = 'comp:false' results = self.lib.items(q) - self.assert_matched(results, ['beets 4 eva']) + self.assert_items_matched(results, ['beets 4 eva']) def test_unknown_field_name_no_results(self): q = 'xyzzy:nonsense' @@ -270,7 +269,7 @@ class GetTest(DummyDataTestCase): q = u'title:caf\xe9' results = self.lib.items(q) - self.assert_matched(results, [u'caf\xe9']) + self.assert_items_matched(results, [u'caf\xe9']) def test_numeric_search_positive(self): q = dbcore.query.NumericQuery('year', '2001') @@ -354,7 +353,7 @@ class PathQueryTest(_common.LibTestCase, TestHelper, AssertsMixin): def test_path_exact_match(self): q = 'path:/a/b/c.mp3' results = self.lib.items(q) - self.assert_matched(results, ['path item']) + self.assert_items_matched(results, ['path item']) results = self.lib.albums(q) self.assert_albums_matched(results, []) @@ -362,7 +361,7 @@ class PathQueryTest(_common.LibTestCase, TestHelper, AssertsMixin): def test_parent_directory_no_slash(self): q = 'path:/a' results = self.lib.items(q) - self.assert_matched(results, ['path item']) + self.assert_items_matched(results, ['path item']) results = self.lib.albums(q) self.assert_albums_matched(results, ['path album']) @@ -370,7 +369,7 @@ class PathQueryTest(_common.LibTestCase, TestHelper, AssertsMixin): def test_parent_directory_with_slash(self): q = 'path:/a/' results = self.lib.items(q) - self.assert_matched(results, ['path item']) + self.assert_items_matched(results, ['path item']) results = self.lib.albums(q) self.assert_albums_matched(results, ['path album']) @@ -378,7 +377,7 @@ class PathQueryTest(_common.LibTestCase, TestHelper, AssertsMixin): def test_no_match(self): q = 'path:/xyzzy/' results = self.lib.items(q) - self.assert_matched(results, []) + self.assert_items_matched(results, []) results = self.lib.albums(q) self.assert_albums_matched(results, []) @@ -386,7 +385,7 @@ class PathQueryTest(_common.LibTestCase, TestHelper, AssertsMixin): def test_fragment_no_match(self): q = 'path:/b/' results = self.lib.items(q) - self.assert_matched(results, []) + self.assert_items_matched(results, []) results = self.lib.albums(q) self.assert_albums_matched(results, []) @@ -394,7 +393,7 @@ class PathQueryTest(_common.LibTestCase, TestHelper, AssertsMixin): def test_nonnorm_path(self): q = 'path:/x/../a/b' results = self.lib.items(q) - self.assert_matched(results, ['path item']) + self.assert_items_matched(results, ['path item']) results = self.lib.albums(q) self.assert_albums_matched(results, ['path album']) @@ -402,7 +401,7 @@ class PathQueryTest(_common.LibTestCase, TestHelper, AssertsMixin): def test_slashed_query_matches_path(self): q = '/a/b' results = self.lib.items(q) - self.assert_matched(results, ['path item']) + self.assert_items_matched(results, ['path item']) results = self.lib.albums(q) self.assert_albums_matched(results, ['path album']) @@ -410,7 +409,7 @@ class PathQueryTest(_common.LibTestCase, TestHelper, AssertsMixin): def test_non_slashed_does_not_match_path(self): q = 'c.mp3' results = self.lib.items(q) - self.assert_matched(results, []) + self.assert_items_matched(results, []) results = self.lib.albums(q) self.assert_albums_matched(results, []) @@ -418,12 +417,12 @@ class PathQueryTest(_common.LibTestCase, TestHelper, AssertsMixin): def test_slashes_in_explicit_field_does_not_match_path(self): q = 'title:/a/b' results = self.lib.items(q) - self.assert_matched(results, []) + self.assert_items_matched(results, []) def test_path_item_regex(self): q = 'path::\\.mp3$' results = self.lib.items(q) - self.assert_matched(results, ['path item']) + self.assert_items_matched(results, ['path item']) def test_path_album_regex(self): q = 'path::b' @@ -435,7 +434,7 @@ class PathQueryTest(_common.LibTestCase, TestHelper, AssertsMixin): album='album with underscore') q = 'path:/a/_' results = self.lib.items(q) - self.assert_matched(results, ['with underscore']) + self.assert_items_matched(results, ['with underscore']) results = self.lib.albums(q) self.assert_albums_matched(results, ['album with underscore']) @@ -445,7 +444,7 @@ class PathQueryTest(_common.LibTestCase, TestHelper, AssertsMixin): album='album with percent') q = 'path:/a/%' results = self.lib.items(q) - self.assert_matched(results, ['with percent']) + self.assert_items_matched(results, ['with percent']) results = self.lib.albums(q) self.assert_albums_matched(results, ['album with percent']) @@ -455,7 +454,7 @@ class PathQueryTest(_common.LibTestCase, TestHelper, AssertsMixin): album='album with backslash') q = r'path:/a/\\x' results = self.lib.items(q) - self.assert_matched(results, ['with backslash']) + self.assert_items_matched(results, ['with backslash']) results = self.lib.albums(q) self.assert_albums_matched(results, ['album with backslash']) @@ -564,11 +563,11 @@ class DefaultSearchFieldsTest(DummyDataTestCase): def test_items_matches_title(self): items = self.lib.items('beets') - self.assert_matched(items, ['beets 4 eva']) + self.assert_items_matched(items, ['beets 4 eva']) def test_items_does_not_match_year(self): items = self.lib.items('2001') - self.assert_matched(items, []) + self.assert_items_matched(items, []) class NoneQueryTest(unittest.TestCase, TestHelper):