From d6ff4308f9357eb27b507dbe014b9e4f8d94b5d1 Mon Sep 17 00:00:00 2001 From: Adrian Sampson Date: Wed, 25 Dec 2013 00:28:26 -0800 Subject: [PATCH] fix tests for new path formatting Slowly expunging the terrible idea that was `pathmod`... --- beets/library.py | 16 +++---- test/_common.py | 20 +++++++++ test/test_db.py | 107 +++++++++++++++++++++++++++++------------------ test/test_ui.py | 2 +- 4 files changed, 93 insertions(+), 52 deletions(-) diff --git a/beets/library.py b/beets/library.py index bda3cfc4e..868ac78ec 100644 --- a/beets/library.py +++ b/beets/library.py @@ -193,13 +193,11 @@ def _orelse(exp1, exp2): # Path element formatting for templating. -def format_for_path(value, key=None, pathmod=None): +def format_for_path(value, key=None): """Sanitize the value for inclusion in a path: replace separators with _, etc. Doesn't guarantee that the whole path will be valid; you should still call `util.sanitize_path` on the complete path. """ - pathmod = pathmod or os.path - if isinstance(value, str): value = value.decode('utf8', 'ignore') elif key in ('track', 'tracktotal', 'disc', 'disctotal'): @@ -776,7 +774,7 @@ class Item(Model): album = self.get_album() if album: for key in ALBUM_KEYS_ITEM: - mapping[key] = album._get_formatted(key) + mapping[key] = album._get_formatted(key, for_path) # Use the album artist if the track artist is not set and # vice-versa. @@ -825,7 +823,7 @@ class Item(Model): subpath_tmpl = Template(path_format) # Evaluate the selected template. - subpath = self.evaluate_template(subpath_tmpl, True, pathmod) + subpath = self.evaluate_template(subpath_tmpl, True) # Prepare path for output: normalize Unicode characters. if platform == 'darwin': @@ -979,7 +977,7 @@ class Album(Model): item_dir = item_dir or self.item_dir() filename_tmpl = Template(beets.config['art_filename'].get(unicode)) - subpath = format_for_path(self.evaluate_template(filename_tmpl)) + subpath = self.evaluate_template(filename_tmpl, True) subpath = util.sanitize_path(subpath, replacements=self._lib.replacements) subpath = bytestring_path(subpath) @@ -1908,14 +1906,13 @@ class DefaultTemplateFunctions(object): """ _prefix = 'tmpl_' - def __init__(self, item=None, lib=None, pathmod=None): + def __init__(self, item=None, lib=None): """Paramaterize the functions. If `item` or `lib` is None, then some functions (namely, ``aunique``) will always evaluate to the empty string. """ self.item = item self.lib = lib - self.pathmod = pathmod or os.path def functions(self): """Returns a dictionary containing the functions defined in this @@ -2039,8 +2036,7 @@ class DefaultTemplateFunctions(object): return res # Flatten disambiguation value into a string. - disam_value = format_for_path(getattr(album, disambiguator), - disambiguator, self.pathmod) + disam_value = album._get_formatted(disambiguator, True) res = u' [{0}]'.format(disam_value) self.lib._memotable[memokey] = res return res diff --git a/test/_common.py b/test/_common.py index dbe40dbc8..6d3172e76 100644 --- a/test/_common.py +++ b/test/_common.py @@ -19,6 +19,7 @@ import os import logging import tempfile import shutil +from contextlib import contextmanager # Use unittest2 on Python < 2.7. try: @@ -243,3 +244,22 @@ class Bag(object): def __getattr__(self, key): return self.fields.get(key) + + +# Platform mocking. + +@contextmanager +def platform_windows(): + import ntpath + old_path = os.path + os.path = ntpath + yield + os.path = old_path + +@contextmanager +def platform_posix(): + import posixpath + old_path = os.path + os.path = posixpath + yield + os.path = old_path diff --git a/test/test_db.py b/test/test_db.py index 798cab7f5..94900de93 100644 --- a/test/test_db.py +++ b/test/test_db.py @@ -16,8 +16,6 @@ """ import os import sqlite3 -import ntpath -import posixpath import shutil import re import unicodedata @@ -221,22 +219,26 @@ class DestinationTest(_common.TestCase): def test_distination_windows_removes_both_separators(self): self.i.title = 'one \\ two / three.mp3' - p = self.i.destination(pathmod=ntpath) + with _common.platform_windows(): + p = self.i.destination() self.assertFalse('one \\ two' in p) self.assertFalse('one / two' in p) self.assertFalse('two \\ three' in p) self.assertFalse('two / three' in p) def test_sanitize_unix_replaces_leading_dot(self): - p = util.sanitize_path(u'one/.two/three', posixpath) + with _common.platform_posix(): + p = util.sanitize_path(u'one/.two/three') self.assertFalse('.' in p) def test_sanitize_windows_replaces_trailing_dot(self): - p = util.sanitize_path(u'one/two./three', ntpath) + with _common.platform_windows(): + p = util.sanitize_path(u'one/two./three') self.assertFalse('.' in p) def test_sanitize_windows_replaces_illegal_chars(self): - p = util.sanitize_path(u':*?"<>|', ntpath) + with _common.platform_windows(): + p = util.sanitize_path(u':*?"<>|') self.assertFalse(':' in p) self.assertFalse('*' in p) self.assertFalse('?' in p) @@ -322,33 +324,39 @@ class DestinationTest(_common.TestCase): self.assertEqual(self.i.destination(), np('one/three')) def test_sanitize_windows_replaces_trailing_space(self): - p = util.sanitize_path(u'one/two /three', ntpath) + with _common.platform_windows(): + p = util.sanitize_path(u'one/two /three') self.assertFalse(' ' in p) - def test_component_sanitize_replaces_separators(self): - name = posixpath.join('a', 'b') - newname = beets.library.format_for_path(name, None, posixpath) - self.assertNotEqual(name, newname) + def test_component_sanitize_does_not_replace_separators(self): + with _common.platform_posix(): + name = os.path.join('a', 'b') + newname = beets.library.format_for_path(name, None) + self.assertEqual(name, newname) def test_component_sanitize_pads_with_zero(self): - name = beets.library.format_for_path(1, 'track', posixpath) + with _common.platform_posix(): + name = beets.library.format_for_path(1, 'track') self.assertTrue(name.startswith('0')) def test_component_sanitize_uses_kbps_bitrate(self): - val = beets.library.format_for_path(12345, 'bitrate', posixpath) + with _common.platform_posix(): + val = beets.library.format_for_path(12345, 'bitrate') self.assertEqual(val, u'12kbps') def test_component_sanitize_uses_khz_samplerate(self): - val = beets.library.format_for_path(12345, 'samplerate', posixpath) + with _common.platform_posix(): + val = beets.library.format_for_path(12345, 'samplerate') self.assertEqual(val, u'12kHz') def test_component_sanitize_datetime(self): - val = beets.library.format_for_path(1368302461.210265, 'added', - posixpath) + with _common.platform_posix(): + val = beets.library.format_for_path(1368302461.210265, 'added') self.assertTrue(val.startswith('2013')) def test_component_sanitize_none(self): - val = beets.library.format_for_path(None, 'foo', posixpath) + with _common.platform_posix(): + val = beets.library.format_for_path(None, 'foo') self.assertEqual(val, u'') def test_artist_falls_back_to_albumartist(self): @@ -380,19 +388,22 @@ class DestinationTest(_common.TestCase): self.assertEqual(p.rsplit(os.path.sep, 1)[1], 'something') def test_sanitize_path_works_on_empty_string(self): - p = util.sanitize_path(u'', posixpath) + with _common.platform_posix(): + p = util.sanitize_path(u'') self.assertEqual(p, u'') def test_sanitize_with_custom_replace_overrides_built_in_sub(self): - p = util.sanitize_path(u'a/.?/b', posixpath, [ - (re.compile(ur'foo'), u'bar'), - ]) + with _common.platform_posix(): + p = util.sanitize_path(u'a/.?/b', None, [ + (re.compile(ur'foo'), u'bar'), + ]) self.assertEqual(p, u'a/.?/b') def test_sanitize_with_custom_replace_adds_replacements(self): - p = util.sanitize_path(u'foo/bar', posixpath, [ - (re.compile(ur'foo'), u'bar'), - ]) + with _common.platform_posix(): + p = util.sanitize_path(u'foo/bar', None, [ + (re.compile(ur'foo'), u'bar'), + ]) self.assertEqual(p, u'bar/bar') def test_unicode_normalized_nfd_on_mac(self): @@ -434,7 +445,9 @@ class PathFormattingMixin(object): def _assert_dest(self, dest, i=None): if i is None: i = self.i - self.assertEqual(i.destination(pathmod=posixpath), dest) + with _common.platform_posix(): + actual = i.destination() + self.assertEqual(actual, dest) class DestinationFunctionTest(_common.TestCase, PathFormattingMixin): @@ -552,21 +565,24 @@ class DisambiguationTest(_common.TestCase, PathFormattingMixin): class PathConversionTest(_common.TestCase): def test_syspath_windows_format(self): - path = ntpath.join('a', 'b', 'c') - outpath = util.syspath(path, pathmod=ntpath) + with _common.platform_windows(): + path = os.path.join('a', 'b', 'c') + outpath = util.syspath(path) self.assertTrue(isinstance(outpath, unicode)) self.assertTrue(outpath.startswith(u'\\\\?\\')) def test_syspath_posix_unchanged(self): - path = posixpath.join('a', 'b', 'c') - outpath = util.syspath(path, pathmod=posixpath) + with _common.platform_posix(): + path = os.path.join('a', 'b', 'c') + outpath = util.syspath(path) self.assertEqual(path, outpath) def _windows_bytestring_path(self, path): old_gfse = sys.getfilesystemencoding sys.getfilesystemencoding = lambda: 'mbcs' try: - return util.bytestring_path(path, ntpath) + with _common.platform_windows(): + return util.bytestring_path(path) finally: sys.getfilesystemencoding = old_gfse @@ -582,26 +598,32 @@ class PathConversionTest(_common.TestCase): class PluginDestinationTest(_common.TestCase): - # Mock the plugins.template_values(item) function. - def _template_values(self, item): - return self._tv_map def setUp(self): super(PluginDestinationTest, self).setUp() + + # Mock beets.plugins.item_field_getters. self._tv_map = {} - self.old_template_values = plugins.template_values - plugins.template_values = self._template_values + def field_getters(): + getters = {} + for key, value in self._tv_map.items(): + getters[key] = lambda _: value + return getters + self.old_field_getters = plugins.item_field_getters + plugins.item_field_getters = field_getters self.lib = beets.library.Library(':memory:') self.lib.directory = '/base' self.lib.path_formats = [('default', u'$artist $foo')] self.i = item(self.lib) + def tearDown(self): super(PluginDestinationTest, self).tearDown() - plugins.template_values = self.old_template_values + plugins.item_field_getters = self.old_field_getters def _assert_dest(self, dest): - self.assertEqual(self.i.destination(pathmod=posixpath), - '/base/' + dest) + with _common.platform_posix(): + the_dest = self.i.destination() + self.assertEqual(the_dest, '/base/' + dest) def test_undefined_value_not_substituted(self): self._assert_dest('the artist $foo') @@ -934,15 +956,18 @@ class PathStringTest(_common.TestCase): class PathTruncationTest(_common.TestCase): def test_truncate_bytestring(self): - p = util.truncate_path('abcde/fgh', posixpath, 4) + with _common.platform_posix(): + p = util.truncate_path('abcde/fgh', None, 4) self.assertEqual(p, 'abcd/fgh') def test_truncate_unicode(self): - p = util.truncate_path(u'abcde/fgh', posixpath, 4) + with _common.platform_posix(): + p = util.truncate_path(u'abcde/fgh', None, 4) self.assertEqual(p, u'abcd/fgh') def test_truncate_preserves_extension(self): - p = util.truncate_path(u'abcde/fgh.ext', posixpath, 5) + with _common.platform_posix(): + p = util.truncate_path(u'abcde/fgh.ext', None, 5) self.assertEqual(p, u'abcde/f.ext') diff --git a/test/test_ui.py b/test/test_ui.py index 1c024def3..b6c13fb7f 100644 --- a/test/test_ui.py +++ b/test/test_ui.py @@ -104,7 +104,7 @@ class ListTest(_common.TestCase): self.assertTrue(u'1' in out) self.assertTrue(u'the album' in out) self.assertTrue(u'the artist' in out) - self.assertEqual(u'the artist - the album - 1', out.strip()) + self.assertEqual(u'the artist - the album - 0001', out.strip()) def test_list_album_format(self): self._run_list(album=True, fmt='$genre')