mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Merge branch 'csv_module' of https://github.com/jamesbroadhead/calibre into jamesbroadhead-csv_module
This commit is contained in:
commit
5299c936e5
1
.gitignore
vendored
1
.gitignore
vendored
@ -5,6 +5,7 @@
|
|||||||
.bzr
|
.bzr
|
||||||
.bzrignore
|
.bzrignore
|
||||||
.build-cache
|
.build-cache
|
||||||
|
.cache
|
||||||
src/calibre/plugins
|
src/calibre/plugins
|
||||||
resources/images.qrc
|
resources/images.qrc
|
||||||
manual/generated
|
manual/generated
|
||||||
|
@ -25,5 +25,8 @@ before_install:
|
|||||||
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then sudo mkdir -p $SWBASE && sudo chown $USER $SWBASE; fi
|
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then sudo mkdir -p $SWBASE && sudo chown $USER $SWBASE; fi
|
||||||
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then curl https://download.calibre-ebook.com/travis/sw-osx.tar.bz2 | tar xj -C $SWBASE; fi
|
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then curl https://download.calibre-ebook.com/travis/sw-osx.tar.bz2 | tar xj -C $SWBASE; fi
|
||||||
- npm install --no-optional rapydscript-ng && echo $PATH && which rapydscript && rapydscript --version
|
- npm install --no-optional rapydscript-ng && echo $PATH && which rapydscript && rapydscript --version
|
||||||
|
- python $SW/bin/easy_install --user "pip==9.0.1"
|
||||||
|
- pip install --user -r requirements.txt
|
||||||
- python setup.py bootstrap --ephemeral
|
- python setup.py bootstrap --ephemeral
|
||||||
|
|
||||||
script: python setup.py test
|
script: python setup.py test
|
||||||
|
@ -4,7 +4,7 @@ os: Visual Studio 2015
|
|||||||
clone_folder: C:\calibre
|
clone_folder: C:\calibre
|
||||||
clone_depth: 5
|
clone_depth: 5
|
||||||
branches:
|
branches:
|
||||||
only:
|
only:
|
||||||
- vs2015
|
- vs2015
|
||||||
|
|
||||||
cache:
|
cache:
|
||||||
@ -19,7 +19,9 @@ platform:
|
|||||||
- x64
|
- x64
|
||||||
|
|
||||||
before_build:
|
before_build:
|
||||||
- C:\Python35-x64\python.exe setup/win-ci.py sw
|
- C:\Python35-x64\python.exe setup/win-ci.py sw
|
||||||
|
- C:\sw\private\python\easy_install.exe --user "pip==9.0.1"
|
||||||
|
- C:\sw\private\python\pip install --user -r requirements.txt
|
||||||
|
|
||||||
build_script:
|
build_script:
|
||||||
- C:\sw\private\python\python.exe setup/win-ci.py build
|
- C:\sw\private\python\python.exe setup/win-ci.py build
|
||||||
|
2
requirements.txt
Normal file
2
requirements.txt
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
# Note, this file is work-in-progress, and is partial only.
|
||||||
|
mock==2.0.0
|
@ -8,7 +8,7 @@ import unittest
|
|||||||
|
|
||||||
from setup import Command
|
from setup import Command
|
||||||
|
|
||||||
TEST_MODULES = frozenset('srv db polish opf css docx cfi matcher icu smartypants build misc'.split())
|
TEST_MODULES = frozenset('srv db polish opf css docx cfi matcher icu smartypants build misc library'.split())
|
||||||
|
|
||||||
def find_tests(which_tests=None):
|
def find_tests(which_tests=None):
|
||||||
ans = []
|
ans = []
|
||||||
@ -64,10 +64,13 @@ def find_tests(which_tests=None):
|
|||||||
a(find_tests())
|
a(find_tests())
|
||||||
from calibre.utils.shared_file import find_tests
|
from calibre.utils.shared_file import find_tests
|
||||||
a(find_tests())
|
a(find_tests())
|
||||||
|
if ok('library'):
|
||||||
|
a(unittest.TestLoader().discover('src/calibre/library'))
|
||||||
|
|
||||||
tests = unittest.TestSuite(ans)
|
tests = unittest.TestSuite(ans)
|
||||||
return tests
|
return tests
|
||||||
|
|
||||||
|
|
||||||
class Test(Command):
|
class Test(Command):
|
||||||
|
|
||||||
description = 'Run the calibre test suite'
|
description = 'Run the calibre test suite'
|
||||||
|
@ -7,7 +7,7 @@ __docformat__ = 'restructuredtext en'
|
|||||||
Command line interface to the calibre database.
|
Command line interface to the calibre database.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
import sys, os, cStringIO, re
|
import cStringIO, csv, os, re, sys
|
||||||
import unicodedata
|
import unicodedata
|
||||||
from textwrap import TextWrapper
|
from textwrap import TextWrapper
|
||||||
from optparse import OptionValueError, OptionGroup
|
from optparse import OptionValueError, OptionGroup
|
||||||
@ -1362,18 +1362,6 @@ def command_check_library(args, dbpath):
|
|||||||
else:
|
else:
|
||||||
exts = [f.strip() for f in opts.exts.split(',') if f.strip()]
|
exts = [f.strip() for f in opts.exts.split(',') if f.strip()]
|
||||||
|
|
||||||
def print_one(checker, check):
|
|
||||||
attr = check[0]
|
|
||||||
list = getattr(checker, attr, None)
|
|
||||||
if list is None:
|
|
||||||
return
|
|
||||||
if opts.csv:
|
|
||||||
for i in list:
|
|
||||||
print check[1] + ',' + i[0] + ',' + i[1]
|
|
||||||
else:
|
|
||||||
print check[1]
|
|
||||||
for i in list:
|
|
||||||
print ' %-40.40s - %-40.40s'%(i[0], i[1])
|
|
||||||
|
|
||||||
if not LibraryDatabase.exists_at(dbpath):
|
if not LibraryDatabase.exists_at(dbpath):
|
||||||
prints('No library found at', dbpath, file=sys.stderr)
|
prints('No library found at', dbpath, file=sys.stderr)
|
||||||
@ -1383,7 +1371,25 @@ def command_check_library(args, dbpath):
|
|||||||
checker = CheckLibrary(dbpath, db)
|
checker = CheckLibrary(dbpath, db)
|
||||||
checker.scan_library(names, exts)
|
checker.scan_library(names, exts)
|
||||||
for check in checks:
|
for check in checks:
|
||||||
print_one(checker, check)
|
_print_check_library_results(checker, check, opts)
|
||||||
|
|
||||||
|
|
||||||
|
def _print_check_library_results(checker, check, opts):
|
||||||
|
attr = check[0]
|
||||||
|
list = getattr(checker, attr, None)
|
||||||
|
if list is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
if opts.csv:
|
||||||
|
to_output = [(check[1], i[0], i[1]) for i in list]
|
||||||
|
csv_print = csv.writer(sys.stdout)
|
||||||
|
for line in to_output:
|
||||||
|
csv_print.writerow(line)
|
||||||
|
|
||||||
|
else:
|
||||||
|
print check[1]
|
||||||
|
for i in list:
|
||||||
|
print ' %-40.40s - %-40.40s'%(i[0], i[1])
|
||||||
|
|
||||||
|
|
||||||
def restore_database_option_parser():
|
def restore_database_option_parser():
|
||||||
|
@ -1,105 +0,0 @@
|
|||||||
#!/usr/bin/env python2
|
|
||||||
__license__ = 'GPL v3'
|
|
||||||
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
|
|
||||||
__docformat__ = 'restructuredtext en'
|
|
||||||
|
|
||||||
'''
|
|
||||||
Unit tests for database layer.
|
|
||||||
'''
|
|
||||||
|
|
||||||
import sys, unittest, os, cStringIO
|
|
||||||
from itertools import repeat
|
|
||||||
|
|
||||||
from calibre.ptempfile import PersistentTemporaryDirectory
|
|
||||||
from calibre.library.database2 import LibraryDatabase2
|
|
||||||
from calibre.ebooks.metadata import MetaInformation
|
|
||||||
|
|
||||||
|
|
||||||
class DBTest(unittest.TestCase):
|
|
||||||
|
|
||||||
img = '\xff\xd8\xff\xe0\x00\x10JFIF\x00\x01\x01\x01\x00d\x00d\x00\x00\xff\xdb\x00C\x00\x05\x03\x04\x04\x04\x03\x05\x04\x04\x04\x05\x05\x05\x06\x07\x0c\x08\x07\x07\x07\x07\x0f\x0b\x0b\t\x0c\x11\x0f\x12\x12\x11\x0f\x11\x11\x13\x16\x1c\x17\x13\x14\x1a\x15\x11\x11\x18!\x18\x1a\x1d\x1d\x1f\x1f\x1f\x13\x17"$"\x1e$\x1c\x1e\x1f\x1e\xff\xdb\x00C\x01\x05\x05\x05\x07\x06\x07\x0e\x08\x08\x0e\x1e\x14\x11\x14\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\xff\xc0\x00\x11\x08\x00\x01\x00\x01\x03\x01\x11\x00\x02\x11\x01\x03\x11\x01\xff\xc4\x00\x14\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\xff\xc4\x00\x14\x10\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xc4\x00\x14\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06\xff\xc4\x00\x14\x11\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xda\x00\x0c\x03\x01\x00\x02\x11\x03\x11\x00?\x00p\xf9+\xff\xd9' # noqa
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
self.tdir = PersistentTemporaryDirectory('_calibre_dbtest')
|
|
||||||
self.db = LibraryDatabase2(self.tdir)
|
|
||||||
f = open(os.path.join(self.tdir, 'test.txt'), 'w+b')
|
|
||||||
f.write('test')
|
|
||||||
paths = list(repeat(f, 3))
|
|
||||||
formats = list(repeat('txt', 3))
|
|
||||||
m1 = MetaInformation('Test Ebook 1', ['Test Author 1'])
|
|
||||||
m1.tags = ['tag1', 'tag2']
|
|
||||||
m1.publisher = 'Test Publisher 1'
|
|
||||||
m1.rating = 2
|
|
||||||
m1.series = 'Test Series 1'
|
|
||||||
m1.series_index = 3
|
|
||||||
m1.author_sort = 'as1'
|
|
||||||
m1.isbn = 'isbn1'
|
|
||||||
m1.cover_data = ('jpg', self.img)
|
|
||||||
m2 = MetaInformation('Test Ebook 2', ['Test Author 2'])
|
|
||||||
m2.tags = ['tag3', 'tag4']
|
|
||||||
m2.publisher = 'Test Publisher 2'
|
|
||||||
m2.rating = 3
|
|
||||||
m2.series = 'Test Series 2'
|
|
||||||
m2.series_index = 1
|
|
||||||
m2.author_sort = 'as1'
|
|
||||||
m2.isbn = 'isbn1'
|
|
||||||
self.db.add_books(paths, formats, [m1, m2, m2], add_duplicates=True)
|
|
||||||
self.m1, self.m2 = m1, m2
|
|
||||||
|
|
||||||
def testAdding(self):
|
|
||||||
m1, m2 = self.db.get_metadata(1, True), self.db.get_metadata(2, True)
|
|
||||||
for p in ('title', 'authors', 'publisher', 'rating', 'series',
|
|
||||||
'series_index', 'author_sort', 'isbn', 'tags'):
|
|
||||||
|
|
||||||
def ga(mi, p):
|
|
||||||
val = getattr(mi, p)
|
|
||||||
if isinstance(val, list):
|
|
||||||
val = set(val)
|
|
||||||
return val
|
|
||||||
|
|
||||||
self.assertEqual(ga(self.m1, p), ga(m1, p))
|
|
||||||
self.assertEqual(ga(self.m2, p), ga(m2, p))
|
|
||||||
|
|
||||||
self.assertEqual(self.db.format(1, 'txt', index_is_id=True), 'test')
|
|
||||||
self.assertEqual(self.db.formats(1, index_is_id=True), 'TXT')
|
|
||||||
self.db.add_format(1, 'html', cStringIO.StringIO('<html/>'), index_is_id=True)
|
|
||||||
self.assertEqual(self.db.formats(1, index_is_id=True), 'HTML,TXT')
|
|
||||||
self.db.remove_format(1, 'html', index_is_id=True)
|
|
||||||
self.assertEqual(self.db.formats(1, index_is_id=True), 'TXT')
|
|
||||||
self.assertNotEqual(self.db.cover(1, index_is_id=True), None)
|
|
||||||
self.assertEqual(self.db.cover(2, index_is_id=True), None)
|
|
||||||
|
|
||||||
def testMetadata(self):
|
|
||||||
self.db.refresh('timestamp', True)
|
|
||||||
for x in ('title', 'author_sort', 'series', 'publisher', 'isbn', 'series_index', 'rating'):
|
|
||||||
val = 3 if x in ['rating', 'series_index'] else 'dummy'
|
|
||||||
getattr(self.db, 'set_'+x)(3, val)
|
|
||||||
self.db.refresh_ids([3])
|
|
||||||
self.assertEqual(getattr(self.db, x)(2), val)
|
|
||||||
|
|
||||||
self.db.set_authors(3, ['new auth'])
|
|
||||||
self.db.refresh_ids([3])
|
|
||||||
self.assertEqual('new auth', self.db.authors(2))
|
|
||||||
self.assertEqual(self.db.format(3, 'txt', index_is_id=True), 'test')
|
|
||||||
|
|
||||||
def testSorting(self):
|
|
||||||
self.db.sort('authors', True)
|
|
||||||
self.assertEqual(self.db.authors(0), 'Test Author 1')
|
|
||||||
self.db.sort('rating', False)
|
|
||||||
self.assertEqual(self.db.rating(0), 3)
|
|
||||||
|
|
||||||
|
|
||||||
def suite():
|
|
||||||
return unittest.TestLoader().loadTestsFromTestCase(DBTest)
|
|
||||||
|
|
||||||
|
|
||||||
def test():
|
|
||||||
unittest.TextTestRunner(verbosity=2).run(suite())
|
|
||||||
|
|
||||||
|
|
||||||
def main(args=sys.argv):
|
|
||||||
test()
|
|
||||||
return 0
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
sys.exit(main())
|
|
100
src/calibre/library/test_cli.py
Normal file
100
src/calibre/library/test_cli.py
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
#!/usr/bin/env python2
|
||||||
|
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
|
||||||
|
from __future__ import (unicode_literals, division, absolute_import,
|
||||||
|
print_function)
|
||||||
|
|
||||||
|
__license__ = 'GPL v3'
|
||||||
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
|
'''
|
||||||
|
Test the CLI of the calibre database management tool
|
||||||
|
'''
|
||||||
|
import csv
|
||||||
|
import unittest
|
||||||
|
from StringIO import StringIO
|
||||||
|
|
||||||
|
from mock import Mock, patch
|
||||||
|
|
||||||
|
from calibre.library.check_library import CheckLibrary
|
||||||
|
from calibre.library.cli import _print_check_library_results
|
||||||
|
|
||||||
|
|
||||||
|
class PrintCheckLibraryResultsTest(unittest.TestCase):
|
||||||
|
"""
|
||||||
|
Asserts the format of the output to the CLI to avoid regressions
|
||||||
|
"""
|
||||||
|
check_machine_name = 'dummy_check'
|
||||||
|
check_human_name = 'Dummy Check'
|
||||||
|
check = (check_machine_name, check_human_name, True, False)
|
||||||
|
|
||||||
|
@patch('sys.stdout', new_callable=StringIO)
|
||||||
|
def test_prints_nothing_if_no_errors(self, mock_stdout):
|
||||||
|
checker = Mock(name='checker', spec=CheckLibrary)
|
||||||
|
setattr(checker, self.check_machine_name, None)
|
||||||
|
opts = Mock()
|
||||||
|
|
||||||
|
opts.csv = False
|
||||||
|
_print_check_library_results(checker, self.check, opts)
|
||||||
|
self.assertEqual(mock_stdout.getvalue(), '')
|
||||||
|
|
||||||
|
opts.csv = True
|
||||||
|
_print_check_library_results(checker, self.check, opts)
|
||||||
|
self.assertEqual(mock_stdout.getvalue(), '')
|
||||||
|
|
||||||
|
@patch('sys.stdout', new_callable=StringIO)
|
||||||
|
def test_human_readable_output(self, mock_stdout):
|
||||||
|
"""
|
||||||
|
Basic check of the human-readable output.
|
||||||
|
|
||||||
|
Does not test: the full line format, truncation
|
||||||
|
"""
|
||||||
|
checker = Mock(name='checker', speck=CheckLibrary)
|
||||||
|
data = [['first', 'second']]
|
||||||
|
opts = Mock()
|
||||||
|
opts.csv = False
|
||||||
|
setattr(checker, self.check_machine_name, data)
|
||||||
|
_print_check_library_results(checker, self.check, opts)
|
||||||
|
|
||||||
|
result = mock_stdout.getvalue().split('\n')
|
||||||
|
self.assertEqual(len(result), len(data)+2)
|
||||||
|
self.assertEqual(result[0], self.check_human_name)
|
||||||
|
|
||||||
|
result_first = result[1].split('-')[0].strip()
|
||||||
|
result_second = result[1].split('-')[1].strip()
|
||||||
|
|
||||||
|
self.assertEqual(result_first, 'first')
|
||||||
|
self.assertEqual(result_second, 'second')
|
||||||
|
|
||||||
|
self.assertEqual(result[-1], '')
|
||||||
|
|
||||||
|
@patch('sys.stdout', new_callable=StringIO)
|
||||||
|
def test_basic_csv_output(self, mock_stdout):
|
||||||
|
"""
|
||||||
|
Test simple csv output
|
||||||
|
"""
|
||||||
|
checker = Mock(name='checker', speck=CheckLibrary)
|
||||||
|
data = [['first', 'second']]
|
||||||
|
opts = Mock()
|
||||||
|
opts.csv = True
|
||||||
|
setattr(checker, self.check_machine_name, data)
|
||||||
|
_print_check_library_results(checker, self.check, opts)
|
||||||
|
|
||||||
|
result = mock_stdout.getvalue().split('\n')
|
||||||
|
parsed_result = [l for l in csv.reader(result) if l]
|
||||||
|
self.assertEqual(parsed_result, [[self.check_human_name, data[0][0], data[0][1]]])
|
||||||
|
|
||||||
|
@patch('sys.stdout', new_callable=StringIO)
|
||||||
|
def test_escaped_csv_output(self, mock_stdout):
|
||||||
|
"""
|
||||||
|
Test more complex csv output
|
||||||
|
"""
|
||||||
|
checker = Mock(name='checker', speck=CheckLibrary)
|
||||||
|
data = [['I, Caesar', 'second']]
|
||||||
|
opts = Mock()
|
||||||
|
opts.csv = True
|
||||||
|
setattr(checker, self.check_machine_name, data)
|
||||||
|
_print_check_library_results(checker, self.check, opts)
|
||||||
|
|
||||||
|
result = mock_stdout.getvalue().split('\n')
|
||||||
|
parsed_result = [l for l in csv.reader(result) if l]
|
||||||
|
self.assertEqual(parsed_result, [[self.check_human_name, data[0][0], data[0][1]]])
|
Loading…
x
Reference in New Issue
Block a user