%PDF- %PDF-
Direktori : /usr/lib/calibre/calibre/db/tests/ |
Current File : //usr/lib/calibre/calibre/db/tests/filesystem.py |
#!/usr/bin/env python3 __license__ = 'GPL v3' __copyright__ = '2013, Kovid Goyal <kovid at kovidgoyal.net>' __docformat__ = 'restructuredtext en' import unittest, os from io import BytesIO from calibre.constants import iswindows from calibre.db.tests.base import BaseTest from calibre.ptempfile import TemporaryDirectory class FilesystemTest(BaseTest): def get_filesystem_data(self, cache, book_id): fmts = cache.field_for('formats', book_id) ans = {} for fmt in fmts: buf = BytesIO() if cache.copy_format_to(book_id, fmt, buf): ans[fmt] = buf.getvalue() buf = BytesIO() if cache.copy_cover_to(book_id, buf): ans['cover'] = buf.getvalue() return ans def test_metadata_move(self): 'Test the moving of files when title/author change' cl = self.cloned_library cache = self.init_cache(cl) ae, af, sf = self.assertEqual, self.assertFalse, cache.set_field # Test that changing metadata on a book with no formats/cover works ae(sf('title', {3:'moved1'}), {3}) ae(sf('authors', {3:'moved1'}), {3}) ae(sf('title', {3:'Moved1'}), {3}) ae(sf('authors', {3:'Moved1'}), {3}) ae(cache.field_for('title', 3), 'Moved1') ae(cache.field_for('authors', 3), ('Moved1',)) # Now try with a book that has covers and formats orig_data = self.get_filesystem_data(cache, 1) orig_fpath = cache.format_abspath(1, 'FMT1') ae(sf('title', {1:'moved'}), {1}) ae(sf('authors', {1:'moved'}), {1}) ae(sf('title', {1:'Moved'}), {1}) ae(sf('authors', {1:'Moved'}), {1}) ae(cache.field_for('title', 1), 'Moved') ae(cache.field_for('authors', 1), ('Moved',)) cache2 = self.init_cache(cl) for c in (cache, cache2): data = self.get_filesystem_data(c, 1) ae(set(orig_data), set(data)) ae(orig_data, data, 'Filesystem data does not match') ae(c.field_for('path', 1), 'Moved/Moved (1)') ae(c.field_for('path', 3), 'Moved1/Moved1 (3)') fpath = c.format_abspath(1, 'FMT1').replace(os.sep, '/').split('/') ae(fpath[-3:], ['Moved', 'Moved (1)', 'Moved - Moved.fmt1']) af(os.path.exists(os.path.dirname(orig_fpath)), 'Original book folder still exists') # Check that the filesystem reflects fpath (especially on # case-insensitive systems). for x in range(1, 4): base = os.sep.join(fpath[:-x]) part = fpath[-x:][0] self.assertIn(part, os.listdir(base)) @unittest.skipUnless(iswindows, 'Windows only') def test_windows_atomic_move(self): 'Test book file open in another process when changing metadata' cl = self.cloned_library cache = self.init_cache(cl) fpath = cache.format_abspath(1, 'FMT1') with open(fpath, 'rb') as f: with self.assertRaises(IOError): cache.set_field('title', {1:'Moved'}) with self.assertRaises(IOError): cache.remove_books({1}) self.assertNotEqual(cache.field_for('title', 1), 'Moved', 'Title was changed despite file lock') # Test on folder with hardlinks from calibre.ptempfile import TemporaryDirectory from calibre.utils.filenames import hardlink_file, WindowsAtomicFolderMove raw = b'xxx' with TemporaryDirectory() as tdir1, TemporaryDirectory() as tdir2: a, b = os.path.join(tdir1, 'a'), os.path.join(tdir1, 'b') a = os.path.join(tdir1, 'a') with open(a, 'wb') as f: f.write(raw) hardlink_file(a, b) wam = WindowsAtomicFolderMove(tdir1) wam.copy_path_to(a, os.path.join(tdir2, 'a')) wam.copy_path_to(b, os.path.join(tdir2, 'b')) wam.delete_originals() self.assertEqual([], os.listdir(tdir1)) self.assertEqual({'a', 'b'}, set(os.listdir(tdir2))) self.assertEqual(raw, open(os.path.join(tdir2, 'a'), 'rb').read()) self.assertEqual(raw, open(os.path.join(tdir2, 'b'), 'rb').read()) def test_library_move(self): ' Test moving of library ' from calibre.ptempfile import TemporaryDirectory cache = self.init_cache() self.assertIn('metadata.db', cache.get_top_level_move_items()[0]) all_ids = cache.all_book_ids() fmt1 = cache.format(1, 'FMT1') cov = cache.cover(1) odir = cache.backend.library_path with TemporaryDirectory('moved_lib') as tdir: cache.move_library_to(tdir) self.assertIn('moved_lib', cache.backend.library_path) self.assertIn('moved_lib', cache.backend.dbpath) self.assertEqual(fmt1, cache.format(1, 'FMT1')) self.assertEqual(cov, cache.cover(1)) cache.reload_from_db() self.assertEqual(all_ids, cache.all_book_ids()) cache.backend.close() self.assertFalse(os.path.exists(odir)) os.mkdir(odir) # needed otherwise tearDown() fails def test_long_filenames(self): ' Test long file names ' cache = self.init_cache() cache.set_field('title', {1:'a'*10000}) self.assertLessEqual(len(cache.field_for('path', 1)), cache.backend.PATH_LIMIT * 2) cache.set_field('authors', {1:'b'*10000}) self.assertLessEqual(len(cache.field_for('path', 1)), cache.backend.PATH_LIMIT * 2) fpath = cache.format_abspath(1, cache.formats(1)[0]) self.assertLessEqual(len(fpath), len(cache.backend.library_path) + cache.backend.PATH_LIMIT * 4) def test_reserved_names(self): ' Test that folders are not created with a windows reserve name ' cache = self.init_cache() cache.set_field('authors', {1:'con'}) p = cache.field_for('path', 1).replace(os.sep, '/').split('/') self.assertNotIn('con', p) def test_fname_change(self): ' Test the changing of the filename but not the folder name ' cache = self.init_cache() title = 'a'*30 + 'bbb' cache.backend.PATH_LIMIT = 100 cache.set_field('title', {3:title}) cache.add_format(3, 'TXT', BytesIO(b'xxx')) cache.backend.PATH_LIMIT = 40 cache.set_field('title', {3:title}) fpath = cache.format_abspath(3, 'TXT') self.assertEqual(sorted([os.path.basename(fpath)]), sorted(os.listdir(os.path.dirname(fpath)))) def test_export_import(self): from calibre.db.cache import import_library from calibre.utils.exim import Exporter, Importer cache = self.init_cache() for part_size in (1 << 30, 100, 1): with TemporaryDirectory('export_lib') as tdir, TemporaryDirectory('import_lib') as idir: exporter = Exporter(tdir, part_size=part_size) cache.export_library('l', exporter) exporter.commit() importer = Importer(tdir) ic = import_library('l', importer, idir) self.assertEqual(cache.all_book_ids(), ic.all_book_ids()) for book_id in cache.all_book_ids(): self.assertEqual(cache.cover(book_id), ic.cover(book_id), 'Covers not identical for book: %d' % book_id) for fmt in cache.formats(book_id): self.assertEqual(cache.format(book_id, fmt), ic.format(book_id, fmt)) self.assertEqual(cache.format_metadata(book_id, fmt)['mtime'], cache.format_metadata(book_id, fmt)['mtime']) def test_find_books_in_directory(self): from calibre.db.adding import find_books_in_directory, compile_rule strip = lambda files: frozenset({os.path.basename(x) for x in files}) def q(one, two): one, two = {strip(a) for a in one}, {strip(b) for b in two} self.assertEqual(one, two) def r(action='ignore', match_type='startswith', query=''): return {'action':action, 'match_type':match_type, 'query':query} def c(*rules): return tuple(map(compile_rule, rules)) files = ['added.epub', 'ignored.md', 'non-book.other'] q(['added.epub ignored.md'.split()], find_books_in_directory('', True, listdir_impl=lambda x: files)) q([['added.epub'], ['ignored.md']], find_books_in_directory('', False, listdir_impl=lambda x, **k: files)) for rules in ( c(r(query='ignored.'), r(action='add', match_type='endswith', query='.OTHER')), c(r(match_type='glob', query='*.md'), r(action='add', match_type='matches', query=r'.+\.other$')), c(r(match_type='not_startswith', query='IGnored.', action='add'), r(query='ignored.md')), ): q(['added.epub non-book.other'.split()], find_books_in_directory('', True, compiled_rules=rules, listdir_impl=lambda x: files))