Merge pull request #200 from adamcik/feature/local-scanner-fixes
Fix for #189 and other cleanups
This commit is contained in:
commit
6b01e612a3
@ -63,6 +63,16 @@ v0.8 (in development)
|
||||
- Support tracks with only release year, and not a full release date, like e.g.
|
||||
Spotify tracks.
|
||||
|
||||
- Default value of ``LOCAL_MUSIC_PATH`` has been updated to be
|
||||
``$XDG_MUSIC_DIR``, which on most systems this is set to ``$HOME``. Users of
|
||||
local backend that relied on the old default ``~/music`` need to update their
|
||||
settings. Note that the code responsible for finding this music now also
|
||||
ignores UNIX hidden files and folders.
|
||||
|
||||
- File and path settings now support ``$XDG_CACHE_DIR``, ``$XDG_DATA_DIR`` and
|
||||
``$XDG_MUSIC_DIR`` substitution. Defaults for such settings have been updated
|
||||
to use this instead of hidden away defaults.
|
||||
|
||||
**Bug fixes**
|
||||
|
||||
- :issue:`72`: Created a Spotify track proxy that will switch to using loaded
|
||||
@ -80,6 +90,12 @@ v0.8 (in development)
|
||||
|
||||
- Fixed crash on lookup of unknown path when using local backend.
|
||||
|
||||
- :issue:`189` ``LOCAL_MUSIC_PATH`` and path handling in rest of settings has
|
||||
been updated so all of the code now uses the correct value.
|
||||
|
||||
- Fixed incorrect track URIs generated by ``parse_m3u`` code, generated tracks
|
||||
are now relative to ``LOCAL_MUSIC_PATH``.
|
||||
|
||||
|
||||
v0.7.3 (2012-08-11)
|
||||
===================
|
||||
|
||||
@ -7,7 +7,7 @@ import shutil
|
||||
from pykka.actor import ThreadingActor
|
||||
from pykka.registry import ActorRegistry
|
||||
|
||||
from mopidy import audio, core, settings, DATA_PATH
|
||||
from mopidy import audio, core, settings
|
||||
from mopidy.backends import base
|
||||
from mopidy.models import Playlist, Track, Album
|
||||
|
||||
@ -15,13 +15,6 @@ from .translator import parse_m3u, parse_mpd_tag_cache
|
||||
|
||||
logger = logging.getLogger(u'mopidy.backends.local')
|
||||
|
||||
DEFAULT_PLAYLIST_PATH = os.path.join(DATA_PATH, 'playlists')
|
||||
DEFAULT_TAG_CACHE_FILE = os.path.join(DATA_PATH, 'tag_cache')
|
||||
DEFAULT_MUSIC_PATH = str(glib.get_user_special_dir(glib.USER_DIRECTORY_MUSIC))
|
||||
|
||||
if not DEFAULT_MUSIC_PATH or DEFAULT_MUSIC_PATH == os.path.expanduser(u'~'):
|
||||
DEFAULT_MUSIC_PATH = os.path.expanduser(u'~/music')
|
||||
|
||||
|
||||
class LocalBackend(ThreadingActor, base.Backend):
|
||||
"""
|
||||
@ -81,7 +74,7 @@ class LocalPlaybackController(core.PlaybackController):
|
||||
class LocalStoredPlaylistsProvider(base.BaseStoredPlaylistsProvider):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(LocalStoredPlaylistsProvider, self).__init__(*args, **kwargs)
|
||||
self._folder = settings.LOCAL_PLAYLIST_PATH or DEFAULT_PLAYLIST_PATH
|
||||
self._folder = settings.LOCAL_PLAYLIST_PATH
|
||||
self.refresh()
|
||||
|
||||
def lookup(self, uri):
|
||||
@ -95,7 +88,7 @@ class LocalStoredPlaylistsProvider(base.BaseStoredPlaylistsProvider):
|
||||
for m3u in glob.glob(os.path.join(self._folder, '*.m3u')):
|
||||
name = os.path.basename(m3u)[:-len('.m3u')]
|
||||
tracks = []
|
||||
for uri in parse_m3u(m3u):
|
||||
for uri in parse_m3u(m3u, settings.LOCAL_MUSIC_PATH):
|
||||
try:
|
||||
tracks.append(self.backend.library.lookup(uri))
|
||||
except LookupError, e:
|
||||
@ -158,12 +151,11 @@ class LocalLibraryProvider(base.BaseLibraryProvider):
|
||||
self.refresh()
|
||||
|
||||
def refresh(self, uri=None):
|
||||
tag_cache = settings.LOCAL_TAG_CACHE_FILE or DEFAULT_TAG_CACHE_FILE
|
||||
music_folder = settings.LOCAL_MUSIC_PATH or DEFAULT_MUSIC_PATH
|
||||
tracks = parse_mpd_tag_cache(settings.LOCAL_TAG_CACHE_FILE,
|
||||
settings.LOCAL_MUSIC_PATH)
|
||||
|
||||
tracks = parse_mpd_tag_cache(tag_cache, music_folder)
|
||||
|
||||
logger.info('Loading tracks in %s from %s', music_folder, tag_cache)
|
||||
logger.info('Loading tracks in %s from %s', settings.LOCAL_MUSIC_PATH,
|
||||
settings.LOCAL_TAG_CACHE_FILE)
|
||||
|
||||
for track in tracks:
|
||||
self._uri_mapping[track.uri] = track
|
||||
|
||||
@ -7,7 +7,7 @@ from mopidy.models import Track, Artist, Album
|
||||
from mopidy.utils import locale_decode
|
||||
from mopidy.utils.path import path_to_uri
|
||||
|
||||
def parse_m3u(file_path):
|
||||
def parse_m3u(file_path, music_folder):
|
||||
"""
|
||||
Convert M3U file list of uris
|
||||
|
||||
@ -29,8 +29,6 @@ def parse_m3u(file_path):
|
||||
"""
|
||||
|
||||
uris = []
|
||||
folder = os.path.dirname(file_path)
|
||||
|
||||
try:
|
||||
with open(file_path) as m3u:
|
||||
contents = m3u.readlines()
|
||||
@ -48,7 +46,7 @@ def parse_m3u(file_path):
|
||||
if line.startswith('file://'):
|
||||
uris.append(line)
|
||||
else:
|
||||
path = path_to_uri(folder, line)
|
||||
path = path_to_uri(music_folder, line)
|
||||
uris.append(path)
|
||||
|
||||
return uris
|
||||
|
||||
@ -6,7 +6,7 @@ from spotify.manager import SpotifySessionManager as PyspotifySessionManager
|
||||
|
||||
from pykka.registry import ActorRegistry
|
||||
|
||||
from mopidy import audio, get_version, settings, CACHE_PATH
|
||||
from mopidy import audio, get_version, settings
|
||||
from mopidy.backends.base import Backend
|
||||
from mopidy.backends.spotify import BITRATES
|
||||
from mopidy.backends.spotify.container_manager import SpotifyContainerManager
|
||||
@ -22,8 +22,7 @@ logger = logging.getLogger('mopidy.backends.spotify.session_manager')
|
||||
|
||||
|
||||
class SpotifySessionManager(BaseThread, PyspotifySessionManager):
|
||||
cache_location = (settings.SPOTIFY_CACHE_PATH
|
||||
or os.path.join(CACHE_PATH, 'spotify'))
|
||||
cache_location = settings.SPOTIFY_CACHE_PATH
|
||||
settings_location = cache_location
|
||||
appkey_file = os.path.join(os.path.dirname(__file__), 'spotify_appkey.key')
|
||||
user_agent = 'Mopidy %s' % get_version()
|
||||
|
||||
@ -52,7 +52,7 @@ def translator(data):
|
||||
|
||||
class Scanner(object):
|
||||
def __init__(self, folder, data_callback, error_callback=None):
|
||||
self.uris = [path_to_uri(f) for f in find_files(folder)]
|
||||
self.files = find_files(folder)
|
||||
self.data_callback = data_callback
|
||||
self.error_callback = error_callback
|
||||
self.loop = gobject.MainLoop()
|
||||
@ -114,18 +114,19 @@ class Scanner(object):
|
||||
return None
|
||||
|
||||
def next_uri(self):
|
||||
if not self.uris:
|
||||
return self.stop()
|
||||
|
||||
try:
|
||||
uri = path_to_uri(self.files.next())
|
||||
except StopIteration:
|
||||
self.stop()
|
||||
return False
|
||||
self.pipe.set_state(gst.STATE_NULL)
|
||||
self.uribin.set_property('uri', self.uris.pop())
|
||||
self.uribin.set_property('uri', uri)
|
||||
self.pipe.set_state(gst.STATE_PAUSED)
|
||||
return True
|
||||
|
||||
def start(self):
|
||||
if not self.uris:
|
||||
return
|
||||
self.next_uri()
|
||||
self.loop.run()
|
||||
if self.next_uri():
|
||||
self.loop.run()
|
||||
|
||||
def stop(self):
|
||||
self.pipe.set_state(gst.STATE_NULL)
|
||||
|
||||
@ -85,9 +85,8 @@ LASTFM_PASSWORD = u''
|
||||
#:
|
||||
#: Default::
|
||||
#:
|
||||
#: # Defaults to asking glib where music is stored, fallback is ~/music
|
||||
#: LOCAL_MUSIC_PATH = None
|
||||
LOCAL_MUSIC_PATH = None
|
||||
#: LOCAL_MUSIC_PATH = u'$XDG_MUSIC_DIR'
|
||||
LOCAL_MUSIC_PATH = u'$XDG_MUSIC_DIR'
|
||||
|
||||
#: Path to playlist folder with m3u files for local music.
|
||||
#:
|
||||
@ -95,8 +94,8 @@ LOCAL_MUSIC_PATH = None
|
||||
#:
|
||||
#: Default::
|
||||
#:
|
||||
#: LOCAL_PLAYLIST_PATH = None # Implies $XDG_DATA_DIR/mopidy/playlists
|
||||
LOCAL_PLAYLIST_PATH = None
|
||||
#: LOCAL_PLAYLIST_PATH = u'$XDG_DATA_DIR/mopidy/playlists'
|
||||
LOCAL_PLAYLIST_PATH = u'$XDG_DATA_DIR/mopidy/playlists'
|
||||
|
||||
#: Path to tag cache for local music.
|
||||
#:
|
||||
@ -104,8 +103,8 @@ LOCAL_PLAYLIST_PATH = None
|
||||
#:
|
||||
#: Default::
|
||||
#:
|
||||
#: LOCAL_TAG_CACHE_FILE = None # Implies $XDG_DATA_DIR/mopidy/tag_cache
|
||||
LOCAL_TAG_CACHE_FILE = None
|
||||
#: LOCAL_TAG_CACHE_FILE = u'$XDG_DATA_DIR/mopidy/tag_cache'
|
||||
LOCAL_TAG_CACHE_FILE = u'$XDG_DATA_DIR/mopidy/tag_cache'
|
||||
|
||||
#: Sound mixer to use.
|
||||
#:
|
||||
@ -177,7 +176,11 @@ OUTPUT = u'autoaudiosink'
|
||||
#: Path to the Spotify cache.
|
||||
#:
|
||||
#: Used by :mod:`mopidy.backends.spotify`.
|
||||
SPOTIFY_CACHE_PATH = None
|
||||
#:
|
||||
#: Default::
|
||||
#:
|
||||
#: SPOTIFY_CACHE_PATH = u'$XDG_CACHE_DIR/mopidy/spotify'
|
||||
SPOTIFY_CACHE_PATH = u'$XDG_CACHE_DIR/mopidy/spotify'
|
||||
|
||||
#: Your Spotify Premium username.
|
||||
#:
|
||||
@ -194,7 +197,7 @@ SPOTIFY_PASSWORD = u''
|
||||
#: Available values are 96, 160, and 320.
|
||||
#:
|
||||
#: Used by :mod:`mopidy.backends.spotify`.
|
||||
#
|
||||
#:
|
||||
#: Default::
|
||||
#:
|
||||
#: SPOTIFY_BITRATE = 160
|
||||
|
||||
@ -1,11 +1,20 @@
|
||||
import glib
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
import re
|
||||
import string
|
||||
import sys
|
||||
import urllib
|
||||
|
||||
logger = logging.getLogger('mopidy.utils.path')
|
||||
|
||||
XDG_DIRS = {
|
||||
'XDG_CACHE_DIR': glib.get_user_cache_dir(),
|
||||
'XDG_DATA_DIR': glib.get_user_data_dir(),
|
||||
'XDG_MUSIC_DIR': glib.get_user_special_dir(glib.USER_DIRECTORY_MUSIC),
|
||||
}
|
||||
|
||||
|
||||
def get_or_create_folder(folder):
|
||||
folder = os.path.expanduser(folder)
|
||||
if os.path.isfile(folder):
|
||||
@ -16,6 +25,7 @@ def get_or_create_folder(folder):
|
||||
os.makedirs(folder, 0755)
|
||||
return folder
|
||||
|
||||
|
||||
def get_or_create_file(filename):
|
||||
filename = os.path.expanduser(filename)
|
||||
if not os.path.isfile(filename):
|
||||
@ -23,6 +33,7 @@ def get_or_create_file(filename):
|
||||
open(filename, 'w')
|
||||
return filename
|
||||
|
||||
|
||||
def path_to_uri(*paths):
|
||||
path = os.path.join(*paths)
|
||||
path = path.encode('utf-8')
|
||||
@ -30,6 +41,7 @@ def path_to_uri(*paths):
|
||||
return 'file:' + urllib.pathname2url(path)
|
||||
return 'file://' + urllib.pathname2url(path)
|
||||
|
||||
|
||||
def uri_to_path(uri):
|
||||
if sys.platform == 'win32':
|
||||
path = urllib.url2pathname(re.sub('^file:', '', uri))
|
||||
@ -37,6 +49,7 @@ def uri_to_path(uri):
|
||||
path = urllib.url2pathname(re.sub('^file://', '', uri))
|
||||
return path.encode('latin1').decode('utf-8') # Undo double encoding
|
||||
|
||||
|
||||
def split_path(path):
|
||||
parts = []
|
||||
while True:
|
||||
@ -47,21 +60,40 @@ def split_path(path):
|
||||
break
|
||||
return parts
|
||||
|
||||
# pylint: disable = W0612
|
||||
# Unused variable 'dirnames'
|
||||
|
||||
def expand_path(path):
|
||||
path = string.Template(path).safe_substitute(XDG_DIRS)
|
||||
path = os.path.expanduser(path)
|
||||
path = os.path.abspath(path)
|
||||
return path
|
||||
|
||||
|
||||
def find_files(path):
|
||||
if os.path.isfile(path):
|
||||
if not isinstance(path, unicode):
|
||||
path = path.decode('utf-8')
|
||||
yield path
|
||||
if not os.path.basename(path).startswith('.'):
|
||||
yield path
|
||||
else:
|
||||
for dirpath, dirnames, filenames in os.walk(path):
|
||||
# Filter out hidden folders by modifying dirnames in place.
|
||||
for dirname in dirnames:
|
||||
if dirname.startswith('.'):
|
||||
dirnames.remove(dirname)
|
||||
|
||||
for filename in filenames:
|
||||
# Skip hidden files.
|
||||
if filename.startswith('.'):
|
||||
continue
|
||||
|
||||
filename = os.path.join(dirpath, filename)
|
||||
if not isinstance(filename, unicode):
|
||||
filename = filename.decode('utf-8')
|
||||
try:
|
||||
filename = filename.decode('utf-8')
|
||||
except UnicodeDecodeError:
|
||||
filename = filename.decode('latin1')
|
||||
yield filename
|
||||
# pylint: enable = W0612
|
||||
|
||||
|
||||
# FIXME replace with mock usage in tests.
|
||||
class Mtime(object):
|
||||
|
||||
@ -1,14 +1,16 @@
|
||||
# Absolute import needed to import ~/.mopidy/settings.py and not ourselves
|
||||
from __future__ import absolute_import
|
||||
from copy import copy
|
||||
|
||||
import copy
|
||||
import getpass
|
||||
import logging
|
||||
import os
|
||||
from pprint import pformat
|
||||
import pprint
|
||||
import sys
|
||||
|
||||
from mopidy import SettingsError, SETTINGS_PATH, SETTINGS_FILE
|
||||
from mopidy.utils.log import indent
|
||||
from mopidy.utils import log
|
||||
from mopidy.utils import path
|
||||
|
||||
logger = logging.getLogger('mopidy.utils.settings')
|
||||
|
||||
@ -39,7 +41,7 @@ class SettingsProxy(object):
|
||||
|
||||
@property
|
||||
def current(self):
|
||||
current = copy(self.default)
|
||||
current = copy.copy(self.default)
|
||||
current.update(self.local)
|
||||
current.update(self.runtime)
|
||||
return current
|
||||
@ -47,16 +49,18 @@ class SettingsProxy(object):
|
||||
def __getattr__(self, attr):
|
||||
if not self._is_setting(attr):
|
||||
return
|
||||
if attr not in self.current:
|
||||
|
||||
current = self.current # bind locally to avoid copying+updates
|
||||
if attr not in current:
|
||||
raise SettingsError(u'Setting "%s" is not set.' % attr)
|
||||
value = self.current[attr]
|
||||
|
||||
value = current[attr]
|
||||
if isinstance(value, basestring) and len(value) == 0:
|
||||
raise SettingsError(u'Setting "%s" is empty.' % attr)
|
||||
if not value:
|
||||
return value
|
||||
if attr.endswith('_PATH') or attr.endswith('_FILE'):
|
||||
value = os.path.expanduser(value)
|
||||
value = os.path.abspath(value)
|
||||
value = path.expand_path(value)
|
||||
return value
|
||||
|
||||
def __setattr__(self, attr, value):
|
||||
@ -70,7 +74,7 @@ class SettingsProxy(object):
|
||||
self._read_missing_settings_from_stdin(self.current, self.runtime)
|
||||
if self.get_errors():
|
||||
logger.error(u'Settings validation errors: %s',
|
||||
indent(self.get_errors_as_string()))
|
||||
log.indent(self.get_errors_as_string()))
|
||||
raise SettingsError(u'Settings validation failed.')
|
||||
|
||||
def _read_missing_settings_from_stdin(self, current, runtime):
|
||||
@ -194,10 +198,11 @@ def format_settings_list(settings):
|
||||
for (key, value) in sorted(settings.current.iteritems()):
|
||||
default_value = settings.default.get(key)
|
||||
masked_value = mask_value_if_secret(key, value)
|
||||
lines.append(u'%s: %s' % (key, indent(pformat(masked_value), places=2)))
|
||||
lines.append(u'%s: %s' % (key, log.indent(
|
||||
pprint.pformat(masked_value), places=2)))
|
||||
if value != default_value and default_value is not None:
|
||||
lines.append(u' Default: %s' %
|
||||
indent(pformat(default_value), places=4))
|
||||
log.indent(pformat(default_value), places=4))
|
||||
if errors.get(key) is not None:
|
||||
lines.append(u' Error: %s' % errors[key])
|
||||
return '\n'.join(lines)
|
||||
|
||||
@ -9,6 +9,7 @@ from mopidy.models import Track, Artist, Album
|
||||
|
||||
from tests import unittest, path_to_data_dir
|
||||
|
||||
data_dir = path_to_data_dir('')
|
||||
song1_path = path_to_data_dir('song1.mp3')
|
||||
song2_path = path_to_data_dir('song2.mp3')
|
||||
encoded_path = path_to_data_dir(u'æøå.mp3')
|
||||
@ -21,22 +22,32 @@ encoded_uri = path_to_uri(encoded_path)
|
||||
|
||||
class M3UToUriTest(unittest.TestCase):
|
||||
def test_empty_file(self):
|
||||
uris = parse_m3u(path_to_data_dir('empty.m3u'))
|
||||
uris = parse_m3u(path_to_data_dir('empty.m3u'), data_dir)
|
||||
self.assertEqual([], uris)
|
||||
|
||||
def test_basic_file(self):
|
||||
uris = parse_m3u(path_to_data_dir('one.m3u'))
|
||||
uris = parse_m3u(path_to_data_dir('one.m3u'), data_dir)
|
||||
self.assertEqual([song1_uri], uris)
|
||||
|
||||
def test_file_with_comment(self):
|
||||
uris = parse_m3u(path_to_data_dir('comment.m3u'))
|
||||
uris = parse_m3u(path_to_data_dir('comment.m3u'), data_dir)
|
||||
self.assertEqual([song1_uri], uris)
|
||||
|
||||
def test_file_is_relative_to_correct_folder(self):
|
||||
with tempfile.NamedTemporaryFile(delete=False) as tmp:
|
||||
tmp.write('song1.mp3')
|
||||
try:
|
||||
uris = parse_m3u(tmp.name, data_dir)
|
||||
self.assertEqual([song1_uri], uris)
|
||||
finally:
|
||||
if os.path.exists(tmp.name):
|
||||
os.remove(tmp.name)
|
||||
|
||||
def test_file_with_absolute_files(self):
|
||||
with tempfile.NamedTemporaryFile(delete=False) as tmp:
|
||||
tmp.write(song1_path)
|
||||
try:
|
||||
uris = parse_m3u(tmp.name)
|
||||
uris = parse_m3u(tmp.name, data_dir)
|
||||
self.assertEqual([song1_uri], uris)
|
||||
finally:
|
||||
if os.path.exists(tmp.name):
|
||||
@ -48,29 +59,28 @@ class M3UToUriTest(unittest.TestCase):
|
||||
tmp.write('# comment \n')
|
||||
tmp.write(song2_path)
|
||||
try:
|
||||
uris = parse_m3u(tmp.name)
|
||||
uris = parse_m3u(tmp.name, data_dir)
|
||||
self.assertEqual([song1_uri, song2_uri], uris)
|
||||
finally:
|
||||
if os.path.exists(tmp.name):
|
||||
os.remove(tmp.name)
|
||||
|
||||
|
||||
def test_file_with_uri(self):
|
||||
with tempfile.NamedTemporaryFile(delete=False) as tmp:
|
||||
tmp.write(song1_uri)
|
||||
try:
|
||||
uris = parse_m3u(tmp.name)
|
||||
uris = parse_m3u(tmp.name, data_dir)
|
||||
self.assertEqual([song1_uri], uris)
|
||||
finally:
|
||||
if os.path.exists(tmp.name):
|
||||
os.remove(tmp.name)
|
||||
|
||||
def test_encoding_is_latin1(self):
|
||||
uris = parse_m3u(path_to_data_dir('encoding.m3u'))
|
||||
uris = parse_m3u(path_to_data_dir('encoding.m3u'), data_dir)
|
||||
self.assertEqual([encoded_uri], uris)
|
||||
|
||||
def test_open_missing_file(self):
|
||||
uris = parse_m3u(path_to_data_dir('non-existant.m3u'))
|
||||
uris = parse_m3u(path_to_data_dir('non-existant.m3u'), data_dir)
|
||||
self.assertEqual([], uris)
|
||||
|
||||
|
||||
|
||||
BIN
tests/data/.blank.mp3
Normal file
BIN
tests/data/.blank.mp3
Normal file
Binary file not shown.
0
tests/data/.hidden/.gitignore
vendored
Normal file
0
tests/data/.hidden/.gitignore
vendored
Normal file
@ -1,12 +1,12 @@
|
||||
# encoding: utf-8
|
||||
|
||||
import glib
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
from mopidy.utils.path import (get_or_create_folder, mtime,
|
||||
path_to_uri, uri_to_path, split_path, find_files)
|
||||
from mopidy.utils import path
|
||||
|
||||
from tests import unittest, path_to_data_dir
|
||||
|
||||
@ -23,7 +23,7 @@ class GetOrCreateFolderTest(unittest.TestCase):
|
||||
folder = os.path.join(self.parent, 'test')
|
||||
self.assert_(not os.path.exists(folder))
|
||||
self.assert_(not os.path.isdir(folder))
|
||||
created = get_or_create_folder(folder)
|
||||
created = path.get_or_create_folder(folder)
|
||||
self.assert_(os.path.exists(folder))
|
||||
self.assert_(os.path.isdir(folder))
|
||||
self.assertEqual(created, folder)
|
||||
@ -35,7 +35,7 @@ class GetOrCreateFolderTest(unittest.TestCase):
|
||||
self.assert_(not os.path.isdir(level2_folder))
|
||||
self.assert_(not os.path.exists(level3_folder))
|
||||
self.assert_(not os.path.isdir(level3_folder))
|
||||
created = get_or_create_folder(level3_folder)
|
||||
created = path.get_or_create_folder(level3_folder)
|
||||
self.assert_(os.path.exists(level2_folder))
|
||||
self.assert_(os.path.isdir(level2_folder))
|
||||
self.assert_(os.path.exists(level3_folder))
|
||||
@ -43,7 +43,7 @@ class GetOrCreateFolderTest(unittest.TestCase):
|
||||
self.assertEqual(created, level3_folder)
|
||||
|
||||
def test_creating_existing_folder(self):
|
||||
created = get_or_create_folder(self.parent)
|
||||
created = path.get_or_create_folder(self.parent)
|
||||
self.assert_(os.path.exists(self.parent))
|
||||
self.assert_(os.path.isdir(self.parent))
|
||||
self.assertEqual(created, self.parent)
|
||||
@ -52,92 +52,116 @@ class GetOrCreateFolderTest(unittest.TestCase):
|
||||
conflicting_file = os.path.join(self.parent, 'test')
|
||||
open(conflicting_file, 'w').close()
|
||||
folder = os.path.join(self.parent, 'test')
|
||||
self.assertRaises(OSError, get_or_create_folder, folder)
|
||||
self.assertRaises(OSError, path.get_or_create_folder, folder)
|
||||
|
||||
|
||||
class PathToFileURITest(unittest.TestCase):
|
||||
def test_simple_path(self):
|
||||
if sys.platform == 'win32':
|
||||
result = path_to_uri(u'C:/WINDOWS/clock.avi')
|
||||
result = path.path_to_uri(u'C:/WINDOWS/clock.avi')
|
||||
self.assertEqual(result, 'file:///C://WINDOWS/clock.avi')
|
||||
else:
|
||||
result = path_to_uri(u'/etc/fstab')
|
||||
result = path.path_to_uri(u'/etc/fstab')
|
||||
self.assertEqual(result, 'file:///etc/fstab')
|
||||
|
||||
def test_folder_and_path(self):
|
||||
if sys.platform == 'win32':
|
||||
result = path_to_uri(u'C:/WINDOWS/', u'clock.avi')
|
||||
result = path.path_to_uri(u'C:/WINDOWS/', u'clock.avi')
|
||||
self.assertEqual(result, 'file:///C://WINDOWS/clock.avi')
|
||||
else:
|
||||
result = path_to_uri(u'/etc', u'fstab')
|
||||
result = path.path_to_uri(u'/etc', u'fstab')
|
||||
self.assertEqual(result, u'file:///etc/fstab')
|
||||
|
||||
def test_space_in_path(self):
|
||||
if sys.platform == 'win32':
|
||||
result = path_to_uri(u'C:/test this')
|
||||
result = path.path_to_uri(u'C:/test this')
|
||||
self.assertEqual(result, 'file:///C://test%20this')
|
||||
else:
|
||||
result = path_to_uri(u'/tmp/test this')
|
||||
result = path.path_to_uri(u'/tmp/test this')
|
||||
self.assertEqual(result, u'file:///tmp/test%20this')
|
||||
|
||||
def test_unicode_in_path(self):
|
||||
if sys.platform == 'win32':
|
||||
result = path_to_uri(u'C:/æøå')
|
||||
result = path.path_to_uri(u'C:/æøå')
|
||||
self.assertEqual(result, 'file:///C://%C3%A6%C3%B8%C3%A5')
|
||||
else:
|
||||
result = path_to_uri(u'/tmp/æøå')
|
||||
result = path.path_to_uri(u'/tmp/æøå')
|
||||
self.assertEqual(result, u'file:///tmp/%C3%A6%C3%B8%C3%A5')
|
||||
|
||||
|
||||
class UriToPathTest(unittest.TestCase):
|
||||
def test_simple_uri(self):
|
||||
if sys.platform == 'win32':
|
||||
result = uri_to_path('file:///C://WINDOWS/clock.avi')
|
||||
result = path.uri_to_path('file:///C://WINDOWS/clock.avi')
|
||||
self.assertEqual(result, u'C:/WINDOWS/clock.avi')
|
||||
else:
|
||||
result = uri_to_path('file:///etc/fstab')
|
||||
result = path.uri_to_path('file:///etc/fstab')
|
||||
self.assertEqual(result, u'/etc/fstab')
|
||||
|
||||
def test_space_in_uri(self):
|
||||
if sys.platform == 'win32':
|
||||
result = uri_to_path('file:///C://test%20this')
|
||||
result = path.uri_to_path('file:///C://test%20this')
|
||||
self.assertEqual(result, u'C:/test this')
|
||||
else:
|
||||
result = uri_to_path(u'file:///tmp/test%20this')
|
||||
result = path.uri_to_path(u'file:///tmp/test%20this')
|
||||
self.assertEqual(result, u'/tmp/test this')
|
||||
|
||||
def test_unicode_in_uri(self):
|
||||
if sys.platform == 'win32':
|
||||
result = uri_to_path( 'file:///C://%C3%A6%C3%B8%C3%A5')
|
||||
result = path.uri_to_path( 'file:///C://%C3%A6%C3%B8%C3%A5')
|
||||
self.assertEqual(result, u'C:/æøå')
|
||||
else:
|
||||
result = uri_to_path(u'file:///tmp/%C3%A6%C3%B8%C3%A5')
|
||||
result = path.uri_to_path(u'file:///tmp/%C3%A6%C3%B8%C3%A5')
|
||||
self.assertEqual(result, u'/tmp/æøå')
|
||||
|
||||
|
||||
class SplitPathTest(unittest.TestCase):
|
||||
def test_empty_path(self):
|
||||
self.assertEqual([], split_path(''))
|
||||
self.assertEqual([], path.split_path(''))
|
||||
|
||||
def test_single_folder(self):
|
||||
self.assertEqual(['foo'], split_path('foo'))
|
||||
self.assertEqual(['foo'], path.split_path('foo'))
|
||||
|
||||
def test_folders(self):
|
||||
self.assertEqual(['foo', 'bar', 'baz'], split_path('foo/bar/baz'))
|
||||
self.assertEqual(['foo', 'bar', 'baz'], path.split_path('foo/bar/baz'))
|
||||
|
||||
def test_folders(self):
|
||||
self.assertEqual(['foo', 'bar', 'baz'], split_path('foo/bar/baz'))
|
||||
self.assertEqual(['foo', 'bar', 'baz'], path.split_path('foo/bar/baz'))
|
||||
|
||||
def test_initial_slash_is_ignored(self):
|
||||
self.assertEqual(['foo', 'bar', 'baz'], split_path('/foo/bar/baz'))
|
||||
self.assertEqual(['foo', 'bar', 'baz'], path.split_path('/foo/bar/baz'))
|
||||
|
||||
def test_only_slash(self):
|
||||
self.assertEqual([], split_path('/'))
|
||||
self.assertEqual([], path.split_path('/'))
|
||||
|
||||
|
||||
class ExpandPathTest(unittest.TestCase):
|
||||
# TODO: test via mocks?
|
||||
|
||||
def test_empty_path(self):
|
||||
self.assertEqual(os.path.abspath('.'), path.expand_path(''))
|
||||
|
||||
def test_absolute_path(self):
|
||||
self.assertEqual('/tmp/foo', path.expand_path('/tmp/foo'))
|
||||
|
||||
def test_home_dir_expansion(self):
|
||||
self.assertEqual(os.path.expanduser('~/foo'), path.expand_path('~/foo'))
|
||||
|
||||
def test_abspath(self):
|
||||
self.assertEqual(os.path.abspath('./foo'), path.expand_path('./foo'))
|
||||
|
||||
def test_xdg_subsititution(self):
|
||||
self.assertEqual(glib.get_user_data_dir() + '/foo',
|
||||
path.expand_path('$XDG_DATA_DIR/foo'))
|
||||
|
||||
def test_xdg_subsititution_unknown(self):
|
||||
self.assertEqual('/tmp/$XDG_INVALID_DIR/foo',
|
||||
path.expand_path('/tmp/$XDG_INVALID_DIR/foo'))
|
||||
|
||||
|
||||
class FindFilesTest(unittest.TestCase):
|
||||
def find(self, path):
|
||||
return list(find_files(path_to_data_dir(path)))
|
||||
def find(self, value):
|
||||
return list(path.find_files(path_to_data_dir(value)))
|
||||
|
||||
def test_basic_folder(self):
|
||||
self.assert_(self.find(''))
|
||||
@ -156,15 +180,21 @@ class FindFilesTest(unittest.TestCase):
|
||||
self.assert_(is_unicode(name),
|
||||
'%s is not unicode object' % repr(name))
|
||||
|
||||
def test_ignores_hidden_folders(self):
|
||||
self.assertEqual(self.find('.hidden'), [])
|
||||
|
||||
def test_ignores_hidden_files(self):
|
||||
self.assertEqual(self.find('.blank.mp3'), [])
|
||||
|
||||
|
||||
class MtimeTest(unittest.TestCase):
|
||||
def tearDown(self):
|
||||
mtime.undo_fake()
|
||||
path.mtime.undo_fake()
|
||||
|
||||
def test_mtime_of_current_dir(self):
|
||||
mtime_dir = int(os.stat('.').st_mtime)
|
||||
self.assertEqual(mtime_dir, mtime('.'))
|
||||
self.assertEqual(mtime_dir, path.mtime('.'))
|
||||
|
||||
def test_fake_time_is_returned(self):
|
||||
mtime.set_fake_time(123456)
|
||||
self.assertEqual(mtime('.'), 123456)
|
||||
path.mtime.set_fake_time(123456)
|
||||
self.assertEqual(path.mtime('.'), 123456)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user