%PDF- %PDF-
Direktori : /lib/calibre/calibre/ |
Current File : //lib/calibre/calibre/test_build.py |
#!/usr/bin/env python3 __license__ = 'GPL v3' __copyright__ = '2011, Kovid Goyal <kovid@kovidgoyal.net>' __docformat__ = 'restructuredtext en' ''' Test a binary calibre build to ensure that all needed binary images/libraries have loaded. ''' import os, ctypes, sys, unittest, time, shutil from calibre.constants import iswindows, islinux, ismacos, plugins_loc from polyglot.builtins import iteritems is_ci = os.environ.get('CI', '').lower() == 'true' is_sanitized = 'libasan' in os.environ.get('LD_PRELOAD', '') try: import unrardll except ModuleNotFoundError: unrardll = None class BuildTest(unittest.TestCase): @unittest.skipUnless(iswindows and not is_ci, 'DLL loading needs testing only on windows (non-continuous integration)') def test_dlls(self): from calibre_extensions import winutil base = winutil.get_dll_directory() for x in os.listdir(base): if x.lower().endswith('.dll'): try: ctypes.WinDLL(os.path.join(base, x)) except Exception as err: self.assertTrue(False, f'Failed to load DLL {x} with error: {err}') @unittest.skipUnless(islinux, 'DBUS only used on linux') def test_dbus(self): from jeepney.io.blocking import open_dbus_connection if 'DBUS_SESSION_BUS_ADDRESS' in os.environ: bus = open_dbus_connection(bus='SYSTEM') bus.close() bus = open_dbus_connection(bus='SESSION') bus.close() del bus def test_loaders(self): import importlib ldr = importlib.import_module('calibre').__spec__.loader.get_resource_reader() self.assertIn('ebooks', ldr.contents()) try: raw = ldr.open_resource('__init__.py').read() except FileNotFoundError: raw = ldr.open_resource('__init__.pyc').read() self.assertGreater(len(raw), 1024) def test_regex(self): import regex self.assertEqual(regex.findall(r'(?i)(a)(b)', 'ab cd AB 1a1b'), [('a', 'b'), ('A', 'B')]) self.assertEqual(regex.escape('a b', literal_spaces=True), 'a b') def test_hunspell(self): from calibre.spell.dictionary import build_test build_test() def test_pychm(self): from chm.chm import CHMFile, chmlib del CHMFile, chmlib def test_chardet(self): try: from cchardet import detect except ImportError: from chardet import detect raw = 'mūsi Füße'.encode() data = detect(raw) self.assertEqual(data['encoding'].lower(), 'utf-8') self.assertGreater(data['confidence'], 0.5) # The following is used by html5lib from chardet.universaldetector import UniversalDetector detector = UniversalDetector() self.assertTrue(hasattr(detector, 'done')) detector.feed(raw) detector.close() self.assertEqual(detector.result['encoding'], 'utf-8') def test_lzma(self): import lzma lzma.open def test_html5lib(self): import html5lib.html5parser # noqa from html5lib import parse # noqa def test_html5_parser(self): from html5_parser import parse parse('<p>xxx') def test_bs4(self): import soupsieve, bs4 del soupsieve, bs4 @unittest.skipUnless(islinux, 'Speech dispatcher only used on Linux') def test_speech_dispatcher(self): from speechd.client import SSIPClient del SSIPClient def test_zeroconf(self): import zeroconf as z, ifaddr from calibre.devices.smart_device_app.driver import monkeypatch_zeroconf monkeypatch_zeroconf() del z del ifaddr def test_plugins(self): exclusions = set() if islinux and not os.path.exists('/dev/bus/usb'): # libusb fails to initialize in containers without USB subsystems exclusions.update(set('libusb libmtp'.split())) from importlib import import_module from importlib.resources import contents for name in contents('calibre_extensions'): if name in exclusions: if name in ('libusb', 'libmtp'): # Just check that the DLL can be loaded ctypes.CDLL(os.path.join(plugins_loc, name + ('.dylib' if ismacos else '.so'))) continue import_module('calibre_extensions.' + name) def test_lxml(self): from calibre.utils.cleantext import test_clean_xml_chars test_clean_xml_chars() from lxml import etree raw = b'<a/>' root = etree.fromstring(raw, parser=etree.XMLParser(recover=True, no_network=True, resolve_entities=False)) self.assertEqual(etree.tostring(root), raw) from lxml import html html.fromstring("<p>\U0001f63a") def test_certgen(self): from calibre.utils.certgen import create_key_pair create_key_pair() def test_msgpack(self): from calibre.utils.serialize import msgpack_dumps, msgpack_loads from calibre.utils.date import utcnow for obj in ({1:1}, utcnow()): s = msgpack_dumps(obj) self.assertEqual(obj, msgpack_loads(s)) self.assertEqual(type(msgpack_loads(msgpack_dumps(b'b'))), bytes) self.assertEqual(type(msgpack_loads(msgpack_dumps('b'))), str) large = b'x' * (100 * 1024 * 1024) msgpack_loads(msgpack_dumps(large)) @unittest.skipUnless(ismacos, 'FSEvents only present on OS X') def test_fsevents(self): from fsevents import Observer, Stream del Observer, Stream @unittest.skipUnless(iswindows, 'winutil is windows only') def test_winutil(self): import tempfile from calibre import strftime from calibre_extensions import winutil self.assertEqual(winutil.special_folder_path(winutil.CSIDL_APPDATA), winutil.known_folder_path(winutil.FOLDERID_RoamingAppData)) self.assertEqual(winutil.special_folder_path(winutil.CSIDL_LOCAL_APPDATA), winutil.known_folder_path(winutil.FOLDERID_LocalAppData)) self.assertEqual(winutil.special_folder_path(winutil.CSIDL_FONTS), winutil.known_folder_path(winutil.FOLDERID_Fonts)) self.assertEqual(winutil.special_folder_path(winutil.CSIDL_PROFILE), winutil.known_folder_path(winutil.FOLDERID_Profile)) def au(x, name): self.assertTrue( isinstance(x, str), f'{name}() did not return a unicode string, instead returning: {x!r}') for x in 'username temp_path locale_name'.split(): au(getattr(winutil, x)(), x) d = winutil.localeconv() au(d['thousands_sep'], 'localeconv') au(d['decimal_point'], 'localeconv') for k, v in iteritems(d): au(v, k) os.environ['XXXTEST'] = 'YYY' self.assertEqual(os.getenv('XXXTEST'), 'YYY') del os.environ['XXXTEST'] self.assertIsNone(os.getenv('XXXTEST')) for k in os.environ: v = os.getenv(k) if v is not None: au(v, 'getenv-' + k) t = time.localtime() fmt = '%Y%a%b%e%H%M' for fmt in (fmt, fmt.encode('ascii')): x = strftime(fmt, t) au(x, 'strftime') tdir = tempfile.mkdtemp(dir=winutil.temp_path()) path = os.path.join(tdir, 'test-create-file.txt') h = winutil.create_file( path, winutil.GENERIC_READ | winutil.GENERIC_WRITE, 0, winutil.OPEN_ALWAYS, winutil.FILE_ATTRIBUTE_NORMAL) self.assertRaises(OSError, winutil.delete_file, path) del h winutil.delete_file(path) self.assertRaises(OSError, winutil.delete_file, path) self.assertRaises(OSError, winutil.create_file, os.path.join(path, 'cannot'), winutil.GENERIC_READ, 0, winutil.OPEN_ALWAYS, winutil.FILE_ATTRIBUTE_NORMAL) self.assertTrue(winutil.supports_hardlinks(os.path.abspath(os.getcwd())[0] + ':\\')) sz = 23 data = os.urandom(sz) open(path, 'wb').write(data) h = winutil.Handle(0, winutil.ModuleHandle, 'moo') r = repr(h) h2 = winutil.Handle(h.detach(), winutil.ModuleHandle, 'moo') self.assertEqual(r, repr(h2)) h2.close() h = winutil.create_file( path, winutil.GENERIC_READ | winutil.GENERIC_WRITE, 0, winutil.OPEN_ALWAYS, winutil.FILE_ATTRIBUTE_NORMAL) self.assertEqual(winutil.get_file_size(h), sz) self.assertRaises(OSError, winutil.set_file_pointer, h, 23, 23) self.assertEqual(winutil.read_file(h), data) self.assertEqual(winutil.read_file(h), b'') winutil.set_file_pointer(h, 3) self.assertEqual(winutil.read_file(h), data[3:]) self.assertEqual(winutil.nlinks(path), 1) npath = path + '.2' winutil.create_hard_link(npath, path) h.close() self.assertEqual(open(npath, 'rb').read(), data) self.assertEqual(winutil.nlinks(path), 2) winutil.delete_file(path) self.assertEqual(winutil.nlinks(npath), 1) winutil.set_file_attributes(npath, winutil.FILE_ATTRIBUTE_READONLY) self.assertRaises(OSError, winutil.delete_file, npath) winutil.set_file_attributes(npath, winutil.FILE_ATTRIBUTE_NORMAL) winutil.delete_file(npath) self.assertGreater(min(winutil.get_disk_free_space(None)), 0) open(path, 'wb').close() open(npath, 'wb').close() winutil.move_file(path, npath, winutil.MOVEFILE_WRITE_THROUGH | winutil.MOVEFILE_REPLACE_EXISTING) self.assertFalse(os.path.exists(path)) os.remove(npath) dpath = tempfile.mkdtemp(dir=os.path.dirname(path)) dh = winutil.create_file( dpath, winutil.FILE_LIST_DIRECTORY, winutil.FILE_SHARE_READ, winutil.OPEN_EXISTING, winutil.FILE_FLAG_BACKUP_SEMANTICS, ) from threading import Thread, Event started = Event() events = [] def read_changes(): buffer = b'0' * 8192 started.set() events.extend(winutil.read_directory_changes( dh, buffer, True, winutil.FILE_NOTIFY_CHANGE_FILE_NAME | winutil.FILE_NOTIFY_CHANGE_DIR_NAME | winutil.FILE_NOTIFY_CHANGE_ATTRIBUTES | winutil.FILE_NOTIFY_CHANGE_SIZE | winutil.FILE_NOTIFY_CHANGE_LAST_WRITE | winutil.FILE_NOTIFY_CHANGE_SECURITY )) t = Thread(target=read_changes, daemon=True) t.start() started.wait(1) t.join(0.1) testp = os.path.join(dpath, 'test') open(testp, 'w').close() t.join(4) self.assertTrue(events) for actions, path in events: self.assertEqual(os.path.join(dpath, path), testp) dh.close() os.remove(testp) os.rmdir(dpath) del h shutil.rmtree(tdir) m = winutil.create_mutex("test-mutex", False) self.assertRaises(OSError, winutil.create_mutex, 'test-mutex', False) m.close() self.assertEqual(winutil.parse_cmdline('"c:\\test exe.exe" "some arg" 2'), ('c:\\test exe.exe', 'some arg', '2')) def test_sqlite(self): import sqlite3 conn = sqlite3.connect(':memory:') from calibre.library.sqlite import load_c_extensions self.assertTrue(load_c_extensions(conn, True), 'Failed to load sqlite extension') def test_apsw(self): import apsw conn = apsw.Connection(':memory:') conn.close() @unittest.skipIf('SKIP_QT_BUILD_TEST' in os.environ, 'Skipping Qt build test as it causes crashes in the macOS VM') def test_qt(self): if is_sanitized: raise unittest.SkipTest('Skipping Qt build test as sanitizer is enabled') from qt.core import QTimer from qt.core import QApplication from qt.webengine import QWebEnginePage from qt.core import QImageReader, QFontDatabase from qt.core import QNetworkAccessManager from calibre.utils.img import image_from_data, image_to_data, test # Ensure that images can be read before QApplication is constructed. # Note that this requires QCoreApplication.libraryPaths() to return the # path to the Qt plugins which it always does in the frozen build, # because Qt is patched to know the layout of the calibre application # package. On non-frozen builds, it should just work because the # hard-coded paths of the Qt installation should work. If they do not, # then it is a distro problem. fmts = set(map(lambda x: x.data().decode('utf-8'), QImageReader.supportedImageFormats())) # no2to3 testf = {'jpg', 'png', 'svg', 'ico', 'gif', 'webp'} self.assertEqual(testf.intersection(fmts), testf, "Qt doesn't seem to be able to load some of its image plugins. Available plugins: %s" % fmts) data = P('images/blank.png', allow_user_override=False, data=True) img = image_from_data(data) image_from_data(P('catalog/mastheadImage.gif', allow_user_override=False, data=True)) for fmt in 'png bmp jpeg'.split(): d = image_to_data(img, fmt=fmt) image_from_data(d) # Run the imaging tests test() from calibre.gui2 import ensure_app, destroy_app display_env_var = os.environ.pop('DISPLAY', None) try: ensure_app() self.assertGreaterEqual(len(QFontDatabase().families()), 5, 'The QPA headless plugin is not able to locate enough system fonts via fontconfig') from calibre.ebooks.covers import create_cover create_cover('xxx', ['yyy']) na = QNetworkAccessManager() self.assertTrue(hasattr(na, 'sslErrors'), 'Qt not compiled with openssl') if iswindows: from qt.core import QtWin QtWin p = QWebEnginePage() def callback(result): callback.result = result if hasattr(print_callback, 'result'): QApplication.instance().quit() def print_callback(result): print_callback.result = result if hasattr(callback, 'result'): QApplication.instance().quit() p.runJavaScript('1 + 1', callback) p.printToPdf(print_callback) QTimer.singleShot(500000, lambda: QApplication.instance().quit()) QApplication.instance().exec() test_flaky = ismacos and not is_ci if not test_flaky: self.assertTrue(hasattr(callback, 'result'), 'Qt WebEngine failed to run in 5 seconds') self.assertEqual(callback.result, 2, 'Simple JS computation failed') self.assertIn(b'Skia/PDF', bytes(print_callback.result), 'Print to PDF failed') del p del na destroy_app() del QWebEnginePage finally: if display_env_var is not None: os.environ['DISPLAY'] = display_env_var def test_imaging(self): from PIL import Image try: import _imaging, _imagingmath, _imagingft _imaging, _imagingmath, _imagingft except ImportError: from PIL import _imaging, _imagingmath, _imagingft _imaging, _imagingmath, _imagingft i = Image.open(I('lt.png', allow_user_override=False)) self.assertGreaterEqual(i.size, (20, 20)) i = Image.open(P('catalog/DefaultCover.jpg', allow_user_override=False)) self.assertGreaterEqual(i.size, (20, 20)) @unittest.skipUnless(iswindows and not is_ci, 'File dialog helper only used on windows (non-continuous-itegration)') def test_file_dialog_helper(self): from calibre.gui2.win_file_dialogs import test test() @unittest.skipUnless(unrardll, 'Module unrardll is missing') def test_unrar(self): from calibre.utils.unrar import test_basic test_basic() def test_7z(self): from calibre.utils.seven_zip import test_basic test_basic() @unittest.skipUnless(iswindows, 'WPD is windows only') def test_wpd(self): from calibre_extensions import wpd try: wpd.init('calibre', 1, 1, 1) except wpd.NoWPD: pass else: wpd.uninit() def test_tinycss_tokenizer(self): from tinycss.tokenizer import c_tokenize_flat self.assertIsNotNone(c_tokenize_flat, 'tinycss C tokenizer not loaded') @unittest.skipUnless(getattr(sys, 'frozen', False), 'Only makes sense to test executables in frozen builds') def test_executables(self): from calibre.utils.ipc.launch import Worker from calibre.ebooks.pdf.pdftohtml import PDFTOHTML w = Worker({}) self.assertTrue(os.path.exists(w.executable), 'calibre-parallel (%s) does not exist' % w.executable) self.assertTrue(os.path.exists(w.gui_executable), 'calibre-parallel-gui (%s) does not exist' % w.gui_executable) self.assertTrue(os.path.exists(PDFTOHTML), 'pdftohtml (%s) does not exist' % PDFTOHTML) if iswindows: from calibre.devices.usbms.device import eject_exe self.assertTrue(os.path.exists(eject_exe()), 'calibre-eject.exe (%s) does not exist' % eject_exe()) def test_netifaces(self): import netifaces self.assertGreaterEqual(len(netifaces.interfaces()), 1, 'netifaces could find no network interfaces') def test_psutil(self): import psutil psutil.Process(os.getpid()) def test_podofo(self): from calibre.utils.podofo import test_podofo as dotest dotest() @unittest.skipIf(iswindows, 'readline not available on windows') def test_terminal(self): import readline del readline def test_html2text(self): import html2text del html2text def test_markdown(self): from calibre.ebooks.txt.processor import create_markdown_object from calibre.ebooks.conversion.plugins.txt_input import MD_EXTENSIONS create_markdown_object(sorted(MD_EXTENSIONS)) from calibre.library.comments import sanitize_comments_html sanitize_comments_html(b'''<script>moo</script>xxx<img src="http://moo.com/x.jpg">''') def test_feedparser(self): from calibre.web.feeds.feedparser import parse # sgmllib is needed for feedparser parsing malformed feeds # on python3 you can get it by taking it from python2 stdlib and # running 2to3 on it import sgmllib sgmllib, parse def test_openssl(self): import ssl ssl.PROTOCOL_TLSv1_2 if ismacos: cafile = ssl.get_default_verify_paths().cafile if not cafile or not cafile.endswith('/mozilla-ca-certs.pem') or not os.access(cafile, os.R_OK): raise AssertionError('Mozilla CA certs not loaded') def test_multiprocessing(): from multiprocessing import get_context, get_all_start_methods for stype in get_all_start_methods(): if stype == 'fork': continue ctx = get_context(stype) q = ctx.Queue() arg = 'hello' p = ctx.Process(target=q.put, args=(arg,)) p.start() try: x = q.get(timeout=2) except Exception: raise SystemExit(f'Failed to get response from worker process with spawn_type: {stype}') if x != arg: raise SystemExit(f'{x!r} != {arg!r} with spawn_type: {stype}') p.join() def find_tests(): ans = unittest.defaultTestLoader.loadTestsFromTestCase(BuildTest) from calibre.utils.icu_test import find_tests ans.addTests(find_tests()) from tinycss.tests.main import find_tests ans.addTests(find_tests()) from calibre.spell.dictionary import find_tests ans.addTests(find_tests()) from calibre.db.tests.fts import find_tests ans.addTests(find_tests()) return ans def test(): from calibre.utils.run_tests import run_cli run_cli(find_tests()) if __name__ == '__main__': test()