diff --git a/beets/library.py b/beets/library.py index eee26a17e..01873cf96 100644 --- a/beets/library.py +++ b/beets/library.py @@ -61,9 +61,10 @@ def _ancestry(path): """Return a list consisting of path's parent directory, its grandparent, and so on. For instance, _ancestry('/a/b/c') == ['/', '/a', '/a/b'].""" out = [] - while path != '/': + while path and path != '/': path = os.path.dirname(path) - out.insert(0, path) + if path: # don't yield '' + out.insert(0, path) return out def _walk_files(path): @@ -143,6 +144,7 @@ class Item(object): c = self.library.conn.execute( 'select * from items where id=?', (load_id,) ) self._fill_record(c.fetchone()) + self._clear_dirty() c.close() def store(self, store_id=None, store_all=False): diff --git a/test/alltests.py b/test/alltests.py index f568ddb72..d5eba5c79 100755 --- a/test/alltests.py +++ b/test/alltests.py @@ -1,12 +1,15 @@ #!/usr/bin/env python import unittest - -test_modules = ['test_mediafile', 'test_library'] +import os +import re def suite(): s = unittest.TestSuite() - for mod in map(__import__, test_modules): - s.addTest(mod.suite()) + # get the suite() of every module in this directory begining with test_ + for fname in os.listdir('.'): + match = re.match(r'(test_\S+)\.py', fname) + if match: + s.addTest(__import__(match.group(1)).suite()) return s if __name__ == '__main__': diff --git a/test/rsrc/get.blb b/test/rsrc/test.blb similarity index 96% rename from test/rsrc/get.blb rename to test/rsrc/test.blb index de819dd57..81b12afe8 100644 Binary files a/test/rsrc/get.blb and b/test/rsrc/test.blb differ diff --git a/test/test_db.py b/test/test_db.py new file mode 100755 index 000000000..24f636e8f --- /dev/null +++ b/test/test_db.py @@ -0,0 +1,162 @@ +#!/usr/bin/env python + +""" +Tests for non-query database functions of Item. +""" + +import unittest, sys, os +sys.path.append('..') +import beets.library + +def lib(): return beets.library.Library('rsrc' + os.sep + 'test.blb') +def boracay(l): return beets.library.Item(l.conn.execute('select * from items ' + 'where id=3').fetchone(), l) +def item(lib=None): return beets.library.Item({ + 'title': u'the title', + 'artist': u'the artist', + 'album': u'the album', + 'genre': u'the genre', + 'composer': u'the composer', + 'grouping': u'the grouping', + 'year': 1, + 'track': 2, + 'tracktotal': 3, + 'disc': 4, + 'disctotal': 5, + 'lyrics': u'the lyrics', + 'comments': u'the comments', + 'bpm': 6, + 'comp': True, + 'path': 'somepath', +}, lib) + +class LoadTest(unittest.TestCase): + def setUp(self): + self.lib = lib() + self.i = boracay(self.lib) + def tearDown(self): + self.lib.conn.close() + + def test_load_restores_data_from_db(self): + original_title = self.i.title + self.i.title = 'something' + self.i.load() + self.assertEqual(original_title, self.i.title) + + def test_load_clears_dirty_flags(self): + self.i.artist = 'something' + self.i.load() + self.assertTrue(not self.i.dirty['artist']) + +class StoreTest(unittest.TestCase): + def setUp(self): + self.lib = lib() + self.i = boracay(self.lib) + def tearDown(self): + self.lib.conn.close() + + def test_store_changes_database_value(self): + self.i.year = 1987 + self.i.store() + new_year = self.lib.conn.execute('select year from items where ' + 'title="Boracay"').fetchone()['year'] + self.assertEqual(new_year, 1987) + + def test_store_only_writes_dirty_fields(self): + original_genre = self.i.genre + self.i.record['genre'] = 'beatboxing' # change value w/o dirtying + self.i.store() + new_genre = self.lib.conn.execute('select genre from items where ' + 'title="Boracay"').fetchone()['genre'] + self.assertEqual(new_genre, original_genre) + + def test_store_clears_dirty_flags(self): + self.i.composer = 'tvp' + self.i.store() + self.assertTrue(not self.i.dirty['composer']) + +class AddTest(unittest.TestCase): + def setUp(self): + self.lib = lib() + self.i = item(self.lib) + def tearDown(self): + self.lib.conn.close() + + def test_add_inserts_row(self): + self.i.add() + new_grouping = self.lib.conn.execute('select grouping from items ' + 'where composer="the composer"').fetchone()['grouping'] + self.assertEqual(new_grouping, self.i.grouping) + +class RemoveTest(unittest.TestCase): + def setUp(self): + self.lib = lib() + self.i = boracay(self.lib) + def tearDown(self): + self.lib.conn.close() + + def test_remove_deletes_from_db(self): + self.i.remove() + c = self.lib.conn.execute('select * from items where id=3') + self.assertEqual(c.fetchone(), None) + +class GetSetTest(unittest.TestCase): + def setUp(self): + self.i = item() + + def test_set_changes_value(self): + self.i.bpm = 4915 + self.assertEqual(self.i.bpm, 4915) + + def test_set_sets_dirty_flag(self): + self.i.comp = True + self.assertTrue(self.i.dirty['comp']) + +class DestinationTest(unittest.TestCase): + def setUp(self): + self.lib = beets.library.Library(':memory:') + self.i = item(self.lib) + def tearDown(self): + self.lib.conn.close() + + def test_directory_works_with_trailing_slash(self): + self.lib.options['directory'] = 'one/' + self.lib.options['path_format'] = 'two' + self.assertEqual(self.i.destination(), 'one/two') + + def test_directory_works_without_trailing_slash(self): + self.lib.options['directory'] = 'one' + self.lib.options['path_format'] = 'two' + self.assertEqual(self.i.destination(), 'one/two') + + def test_destination_substitues_metadata_values(self): + self.lib.options['directory'] = 'base' + self.lib.options['path_format'] = '$album/$artist $title' + self.i.title = 'three' + self.i.artist = 'two' + self.i.album = 'one' + self.assertEqual(self.i.destination(), 'base/one/two three') + + def test_destination_substitutes_extension(self): + self.lib.options['directory'] = 'base' + self.lib.options['path_format'] = '$extension' + self.i.path = 'hey.audioFormat' + self.assertEqual(self.i.destination(), 'base/audioFormat') + + def test_destination_pads_some_indices(self): + self.lib.options['directory'] = 'base' + self.lib.options['path_format'] = '$track $tracktotal ' \ + '$disc $disctotal $bpm $year' + self.i.track = 1 + self.i.tracktotal = 2 + self.i.disc = 3 + self.i.disctotal = 4 + self.i.bpm = 5 + self.i.year = 6 + self.assertEqual(self.i.destination(), 'base/01 02 03 04 5 6') + +def suite(): + return unittest.TestLoader().loadTestsFromName(__name__) + +if __name__ == '__main__': + unittest.main(defaultTest='suite') \ No newline at end of file diff --git a/test/test_files.py b/test/test_files.py new file mode 100755 index 000000000..03f78a5e0 --- /dev/null +++ b/test/test_files.py @@ -0,0 +1,131 @@ +#!/usr/bin/env python + +""" +Test file manipulation functionality of Item. +""" + +import unittest, shutil, sys, os +sys.path.append('..') +import beets.library +from os.path import join + +def mkfile(path): + open(path, 'w').close() + +class MoveTest(unittest.TestCase): + def setUp(self): + # make a temporary file + self.path = join('rsrc', 'temp.mp3') + shutil.copy(join('rsrc', 'full.mp3'), self.path) + + # add it to a temporary library + self.lib = beets.library.Library(':memory:') + self.i = beets.library.Item.from_path(self.path) + self.i.add(self.lib) + + # set up the destination + self.libdir = join('rsrc', 'testlibdir') + self.lib.options['directory'] = self.libdir + self.lib.options['path_format'] = join('$artist', + '$album', '$title') + self.i.artist = 'one' + self.i.album = 'two' + self.i.title = 'three' + self.dest = join(self.libdir, 'one', 'two', 'three') + + def tearDown(self): + if os.path.exists(self.path): + os.remove(self.path) + if os.path.exists(self.libdir): + shutil.rmtree(self.libdir) + + def test_move_arrives(self): + self.i.move() + self.assertTrue(os.path.exists(self.dest)) + + def test_move_departs(self): + self.i.move() + self.assertTrue(not os.path.exists(self.path)) + + def test_copy_arrives(self): + self.i.move(copy=True) + self.assertTrue(os.path.exists(self.dest)) + + def test_copy_does_not_depart(self): + self.i.move(copy=True) + self.assertTrue(os.path.exists(self.path)) + + def test_move_changes_path(self): + self.i.move() + self.assertEqual(self.i.path,self.dest) + +class DeleteTest(unittest.TestCase): + def setUp(self): + # make a temporary file + self.path = join('rsrc', 'temp.mp3') + shutil.copy(join('rsrc', 'full.mp3'), self.path) + + # add it to a temporary library + self.lib = beets.library.Library(':memory:') + self.i = beets.library.Item.from_path(self.path) + self.i.add(self.lib) + def tearDown(self): + # make sure the temp file is gone + if os.path.exists(self.path): + os.remove(self.path) + + def test_delete_deletes_file(self): + self.i.delete() + self.assertTrue(not os.path.exists(self.path)) + + def test_delete_removes_from_db(self): + self.i.delete() + c = self.lib.conn.execute('select * from items where 1') + self.assertEqual(c.fetchone(), None) + +class WalkTest(unittest.TestCase): + def setUp(self): + # create a directory structure for testing + self.base = join('rsrc', 'temp_walk') + os.mkdir(self.base) + mkfile(join(self.base, 'file')) + os.mkdir(join(self.base, 'dir1')) + mkfile(join(self.base, 'dir1', 'dir1f1')) + mkfile(join(self.base, 'dir1', 'dir1f2')) + os.mkdir(join(self.base, 'dir2')) + mkfile(join(self.base, 'dir2', 'dir2f')) + os.mkdir(join(self.base, 'dir2', 'dir2dir')) + mkfile(join(self.base, 'dir2', 'dir2dir', 'dir2dirf')) + def tearDown(self): + shutil.rmtree(self.base) + + def test_walk_single_file(self): + path = join(self.base, 'file') + s = set(beets.library._walk_files(path)) + self.assertTrue(path in s) + s.remove(path) + self.assertTrue(not s) # s is empty (i.e., contains nothing else) + + def test_walk_flat_directory(self): + path = join(self.base, 'dir1') + s = set(beets.library._walk_files(path)) + for f in (join(path, 'dir1f1'), + join(path, 'dir1f2')): + self.assertTrue(f in s) + s.remove(f) + self.assertTrue(not s) + + def test_walk_hierarchy(self): + path = join(self.base, 'dir2') + s = set(beets.library._walk_files(path)) + for f in (join(path, 'dir2f'), + join(path, 'dir2dir', 'dir2dirf')): + self.assertTrue(f in s) + s.remove(f) + self.assertTrue(not s) + +def suite(): + return unittest.TestLoader().loadTestsFromName(__name__) + +if __name__ == '__main__': + unittest.main(defaultTest='suite') \ No newline at end of file diff --git a/test/test_mediafile.py b/test/test_mediafile.py index 7d62a5bf7..24f91d34b 100755 --- a/test/test_mediafile.py +++ b/test/test_mediafile.py @@ -1,8 +1,14 @@ #!/usr/bin/env python + +""" +Test the MediaFile metadata layer. +""" + import unittest, sys, os, shutil sys.path.append('..') import beets.mediafile + def MakeReadingTest(path, correct_dict, field): class ReadingTest(unittest.TestCase): def setUp(self): diff --git a/test/test_library.py b/test/test_query.py similarity index 97% rename from test/test_library.py rename to test/test_query.py index 82679ca10..2fc85ced1 100755 --- a/test/test_library.py +++ b/test/test_query.py @@ -1,4 +1,9 @@ #!/usr/bin/env python + +""" +Various tests for querying the library database. +""" + import unittest, sys, os sys.path.append('..') import beets.library @@ -43,7 +48,7 @@ class QueryParseTest(unittest.TestCase): class GetTest(unittest.TestCase): def setUp(self): - self.lib = beets.library.Library('rsrc' + os.sep + 'get.blb') + self.lib = beets.library.Library('rsrc' + os.sep + 'test.blb') def assert_matched(self, result_iterator, title): self.assertEqual(result_iterator.next().title, title)