From 932e7a9ab901169bee05912372ffd0af81a9d1dc Mon Sep 17 00:00:00 2001 From: Michal Siedlaczek Date: Tue, 3 Oct 2017 19:54:54 -0400 Subject: [PATCH] Review fixes --- MANIFEST.in | 1 - misc/requirements/requirements-codecov.txt | 8 +- qutebrowser/browser/webengine/langs.tsv | 42 ------ qutebrowser/browser/webengine/spell.py | 139 +++--------------- .../browser/webengine/webenginesettings.py | 26 ++-- qutebrowser/config/configdata.yml | 10 +- qutebrowser/misc/utilcmds.py | 1 - scripts/dev/update_3rdparty.py | 30 +++- scripts/install_dict.py | 116 ++++++++++++++- tests/end2end/conftest.py | 9 +- tests/end2end/features/spell.feature | 28 ++-- tests/end2end/features/test_spell_bdd.py | 36 ----- tests/unit/browser/webengine/test_spell.py | 81 ++-------- tests/unit/scripts/test_install_dict.py | 71 +++++++++ 14 files changed, 281 insertions(+), 317 deletions(-) delete mode 100644 qutebrowser/browser/webengine/langs.tsv create mode 100644 tests/unit/scripts/test_install_dict.py diff --git a/MANIFEST.in b/MANIFEST.in index b8ae5c718..ec906aaf4 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -18,7 +18,6 @@ include tox.ini include qutebrowser.py include misc/cheatsheet.svg include qutebrowser/config/configdata.yml -include qutebrowser/browser/webengine/langs.tsv prune www prune scripts/dev diff --git a/misc/requirements/requirements-codecov.txt b/misc/requirements/requirements-codecov.txt index 86f78562d..c971e2306 100644 --- a/misc/requirements/requirements-codecov.txt +++ b/misc/requirements/requirements-codecov.txt @@ -1,9 +1,9 @@ # This file is automatically generated by scripts/dev/recompile_requirements.py -certifi==2017.4.17 +certifi==2017.7.27.1 chardet==3.0.4 codecov==2.0.9 coverage==4.4.1 -idna==2.5 -requests==2.18.1 -urllib3==1.21.1 +idna==2.6 +requests==2.18.4 +urllib3==1.22 diff --git a/qutebrowser/browser/webengine/langs.tsv b/qutebrowser/browser/webengine/langs.tsv deleted file mode 100644 index ea5a1b17b..000000000 --- a/qutebrowser/browser/webengine/langs.tsv +++ /dev/null @@ -1,42 +0,0 @@ -af-ZA Afrikaans (South Africa) af-ZA-3-0.bdic -bg-BG Bulgarian (Bulgaria) bg-BG-3-0.bdic -ca-ES Catalan (Spain) ca-ES-3-0.bdic -cs-CZ Czech (Czech Republic) cs-CZ-3-0.bdic -da-DK Danish (Denmark) da-DK-3-0.bdic -de-DE German (Germany) de-DE-3-0.bdic -el-GR Greek (Greece) el-GR-3-0.bdic -en-CA English (Canada) en-CA-7-1.bdic -en-GB English (United Kingdom) en-GB-7-1.bdic -en-US English (United States) en-US-7-1.bdic -es-ES Spanish (Spain) es-ES-3-0.bdic -et-EE Estonian (Estonia) et-EE-3-0.bdic -fa-IR Farsi (Iran) fa-IR-7-0.bdic -fo-FO Faroese (Faroe Islands) fo-FO-3-0.bdic -fr-FR French (France) fr-FR-3-0.bdic -he-IL Hebrew (Israel) he-IL-3-0.bdic -hi-IN Hindi (India) hi-IN-3-0.bdic -hr-HR Croatian (Croatia) hr-HR-3-0.bdic -hu-HU Hungarian (Hungary) hu-HU-3-0.bdic -id-ID Indonesian (Indonesia) id-ID-3-0.bdic -it-IT Italian (Italy) it-IT-3-0.bdic -ko Korean ko-3-0.bdic -lt-LT Lithuanian (Lithuania) lt-LT-3-0.bdic -lv-LV Latvian (Latvia) lv-LV-3-0.bdic -nb-NO Norwegian (Norway) nb-NO-3-0.bdic -nl-NL Dutch (Netherlands) nl-NL-3-0.bdic -pl-PL Polish (Poland) pl-PL-3-0.bdic -pt-BR Portuguese (Brazil) pt-BR-3-0.bdic -pt-PT Portuguese (Portugal) pt-PT-3-0.bdic -ro-RO Romanian (Romania) ro-RO-3-0.bdic -ru-RU Russian (Russia) ru-RU-3-0.bdic -sh Serbo-Croatian sh-3-0.bdic -sk-SK Slovak (Slovakia) sk-SK-3-0.bdic -sl-SI Slovenian (Slovenia) sl-SI-3-0.bdic -sq Albanian sq-3-0.bdic -sr Serbian sr-3-0.bdic -sv-SE Swedish (Sweden) sv-SE-3-0.bdic -ta-IN Tamil (India) ta-IN-3-0.bdic -tg-TG Tajik (Tajikistan) tg-TG-5-0.bdic -tr-TR Turkish (Turkey) tr-TR-4-0.bdic -uk-UA Ukrainian (Ukraine) uk-UA-3-0.bdic -vi-VN Vietnamese (Viet Nam) vi-VN-3-0.bdic diff --git a/qutebrowser/browser/webengine/spell.py b/qutebrowser/browser/webengine/spell.py index 5f40d189e..aaf346a79 100644 --- a/qutebrowser/browser/webengine/spell.py +++ b/qutebrowser/browser/webengine/spell.py @@ -19,132 +19,29 @@ """Installing and configuring spell-checking for QtWebEngine.""" +import glob import os -from urllib.parse import urljoin -from urllib.request import urlretrieve from PyQt5.QtCore import QLibraryInfo -repository_url = 'https://redirector.gvt1.com/edgedl/chrome/dict/' + +def dictionary_dir(): + """Return the path (str) to the QtWebEngine's dictionaries directory.""" + datapath = QLibraryInfo.location(QLibraryInfo.DataPath) + return os.path.join(datapath, 'qtwebengine_dictionaries') -class Language: +def installed_file(code): + """Return the installed dictionary for the given code. - """Dictionary language specs.""" - - def __init__(self, code, name, file): - self.code = code - self.name = name - self.file = file - - @staticmethod - def from_array(lang_array): - """Create Language object from an array. - - Args: - lang_array: an array of strings containing - the specs of the language in the following format: - [code, name, file] - """ - return Language.from_tuple(tuple(lang_array)) - - @staticmethod - def from_tuple(lang_tuple): - """Create Language object from a tuple. - - Args: - lang_tuple: a tuple of strings containing - the specs of the language in the following format: - (code, name, file) - """ - code, name, file = lang_tuple - return Language(code, name, file) - - @staticmethod - def from_tsv_string(tsv_string): - """Create Language object from a string in tab-separated values format. - - Args: - tsv_string: a string containing - the specs of the language in the following format: - "code name file" - """ - lang_array = tsv_string.split('\t') - return Language.from_array(lang_array) - - def __repr__(self): - return 'Language({}, {}, {})'.format(self.code, self.name, self.file) - - -def get_dictionary_dir(): - """Return the path to the QtWebEngine's dictionaries directory.""" - return os.path.join(QLibraryInfo.location(QLibraryInfo.DataPath), - 'qtwebengine_dictionaries') - - -def get_language_list_file(): - """Return the path to the file with the list of all available languages.""" - package_dir = os.path.dirname(os.path.abspath(__file__)) - return os.path.join(package_dir, 'langs.tsv') - - -def get_available_languages(): - """Return a list of Language objects of all available languages.""" - with open(get_language_list_file(), 'r', encoding='UTF-8') as file: - return [Language.from_tsv_string(line[:-1]) for line in file] - - -def get_installed_languages(): - """Return a list of Language objects of all installed languages.""" - if not os.path.isdir(get_dictionary_dir()): - return [] - installed_files = [os.path.basename(file) - for file in os.listdir(get_dictionary_dir())] - all_languages = get_available_languages() - return filter_languages(all_languages, installed_files, - by=lambda lang: lang.file, - fail_on_unknown=False) - - -def filter_languages(languages, selected, by=lambda lang: lang.code, - fail_on_unknown=True): - """Filter a list of languages based on an inclusion list. - - Args: - languages: a list of languages to filter - selected: a list of keys to select - by: a function returning the selection key (code by default) - fail_on_unknown: whether to raise an error if there is an unknown - key in selected + Return the filename of the installed dictionary or None + if the dictionary is not installed. """ - filtered_languages = [] - for language in languages: - if by(language) in selected: - filtered_languages.append(language) - selected.remove(by(language)) - if fail_on_unknown and selected: - unknown = ', '.join(selected) - raise ValueError('unknown languages found: {}'.format(unknown)) - return filtered_languages - - -def download_dictionary(url, dest): - urlretrieve(url, dest) - - -def install(languages): - """Install languages.""" - for lang in languages: - try: - print('Installing {}: {}'.format(lang.code, lang.name)) - lang_url = urljoin(repository_url, lang.file) - if not os.path.isdir(get_dictionary_dir()): - print('WARN: {} does not exist, creating the directory'.format( - get_dictionary_dir())) - os.makedirs(get_dictionary_dir()) - print('Downloading {}'.format(lang_url)) - download_dictionary(lang_url, os.path.join(get_dictionary_dir(), - lang.file)) - print('Done.') - except PermissionError as e: - print(e) + pathname = os.path.join(dictionary_dir(), '{}*.bdic'.format(code)) + print(pathname) + matching_dicts = glob.glob(pathname) + if matching_dicts: + with_extension = os.path.basename(matching_dicts[0]) + return os.path.splitext(with_extension)[0] + else: + return None diff --git a/qutebrowser/browser/webengine/webenginesettings.py b/qutebrowser/browser/webengine/webenginesettings.py index 1f21aa2d5..13743e0c8 100644 --- a/qutebrowser/browser/webengine/webenginesettings.py +++ b/qutebrowser/browser/webengine/webenginesettings.py @@ -36,7 +36,7 @@ from PyQt5.QtWebEngineWidgets import (QWebEngineSettings, QWebEngineProfile, QWebEngineScript) from qutebrowser.browser import shared -from qutebrowser.browser.webengine.spell import get_installed_languages +from qutebrowser.browser.webengine import spell from qutebrowser.config import config, websettings from qutebrowser.utils import utils, standarddir, javascript, qtutils, message @@ -134,20 +134,18 @@ class DictionaryLanguageSetter(DefaultProfileSetter): def __init__(self): super().__init__('setSpellCheckLanguages', default=[]) + def _find_installed(self, code): + installed_file = spell.installed_file(code) + if not installed_file: + message.warning('Language {} is not installed.'.format(code)) + return installed_file + def _set(self, value, settings=None): if settings is not None: raise ValueError("'settings' may not be set with " "DictionaryLanguageSetter!") - installed_langs = dict([(lang.code, lang.file) - for lang in get_installed_languages()]) - lang_files = [] - for lang_code in value: - if lang_code in installed_langs: - lang_files.append(installed_langs[lang_code][:-5]) - else: - message.warning('Language {} is not installed.' - .format(lang_code)) - super()._set(lang_files, settings) + filenames = [self._find_installed(code) for code in value] + super()._set([f for f in filenames if f], settings) def _init_stylesheet(profile): @@ -322,7 +320,6 @@ MAPPINGS = { 'scrolling.smooth': Attribute(QWebEngineSettings.ScrollAnimatorEnabled), - } try: @@ -334,8 +331,9 @@ except AttributeError: if qtutils.version_check('5.8'): - MAPPINGS['spell'] = DefaultProfileSetter('setSpellCheckEnabled') - MAPPINGS['spell_languages'] = DictionaryLanguageSetter() + spellcheck_setter = DefaultProfileSetter('setSpellCheckEnabled') + MAPPINGS['spellcheck.enabled'] = spellcheck_setter + MAPPINGS['spellcheck.languages'] = DictionaryLanguageSetter() if qtutils.version_check('5.9'): diff --git a/qutebrowser/config/configdata.yml b/qutebrowser/config/configdata.yml index 77b098e68..efc1d2cd4 100644 --- a/qutebrowser/config/configdata.yml +++ b/qutebrowser/config/configdata.yml @@ -977,7 +977,7 @@ scrolling.smooth: ## spell -spell: +spellcheck.enabled: type: Bool default: true desc: Enable spell checking. @@ -985,7 +985,7 @@ spell: QtWebKit: false QtWebEngine: Qt 5.8 -spell_languages: +spellcheck.languages: type: name: List valtype: @@ -1035,7 +1035,11 @@ spell_languages: - vi-VN: Vietnamese (Viet Nam) none_ok: true default: - desc: Spell checking languages. + desc: >- + Spell checking languages. + + You can check for available languages and install dictionaries using + scripts/install_dict.py. Run the script with -h/--help for instructions. backend: QtWebKit: false QtWebEngine: Qt 5.8 diff --git a/qutebrowser/misc/utilcmds.py b/qutebrowser/misc/utilcmds.py index d8cfe2366..4b6909344 100644 --- a/qutebrowser/misc/utilcmds.py +++ b/qutebrowser/misc/utilcmds.py @@ -33,7 +33,6 @@ import sip from PyQt5.QtCore import QUrl # so it's available for :debug-pyeval from PyQt5.QtWidgets import QApplication # pylint: disable=unused-import -from PyQt5.QtWebEngineWidgets import QWebEngineProfile # pylint: disable=unused-import from qutebrowser.browser import qutescheme from qutebrowser.utils import log, objreg, usertypes, message, debug, utils diff --git a/scripts/dev/update_3rdparty.py b/scripts/dev/update_3rdparty.py index 6fd6810e6..2dd65273a 100755 --- a/scripts/dev/update_3rdparty.py +++ b/scripts/dev/update_3rdparty.py @@ -27,6 +27,7 @@ import urllib.error import shutil import json import os +import sys def get_latest_pdfjs_url(): @@ -110,7 +111,25 @@ def update_ace(): urllib.request.urlcleanup() -def run(ace=False, pdfjs=True, fancy_dmg=False, pdfjs_version=None): +def test_dicts(): + """Test available dictionaries.""" + sys.path.insert(0, os.curdir) + from scripts import install_dict + from qutebrowser.config import configdata + configdata.init() + for lang in install_dict.available_languages(): + sys.stdout.write('Testing dictionary {}... '.format(lang.code)) + lang_url = urllib.parse.urljoin(install_dict.API_URL, lang.file_path) + request = urllib.request.Request(lang_url, method='HEAD') + response = urllib.request.urlopen(request) + if response.status == 200: + print('OK') + else: + print('ERROR: {}'.format(response.status)) + + +def run(ace=False, pdfjs=True, fancy_dmg=False, pdfjs_version=None, + dicts=None): """Update components based on the given arguments.""" if pdfjs: update_pdfjs(pdfjs_version) @@ -118,6 +137,8 @@ def run(ace=False, pdfjs=True, fancy_dmg=False, pdfjs_version=None): update_ace() if fancy_dmg: update_dmg_makefile() + if dicts: + test_dicts() def main(): @@ -129,9 +150,14 @@ def main(): required=False, metavar='VERSION') parser.add_argument('--fancy-dmg', help="Update fancy-dmg Makefile", action='store_true') + parser.add_argument( + '--dicts', '-d', + help='Test whether all available dictionaries ' + 'can be reached at the remote repository.', + required=False, action='store_true') args = parser.parse_args() run(ace=True, pdfjs=True, fancy_dmg=args.fancy_dmg, - pdfjs_version=args.pdfjs) + pdfjs_version=args.pdfjs, dicts=args.dicts) if __name__ == '__main__': diff --git a/scripts/install_dict.py b/scripts/install_dict.py index 7cedf8c23..a5fdabc80 100755 --- a/scripts/install_dict.py +++ b/scripts/install_dict.py @@ -24,9 +24,34 @@ Use: python -m scripts.install_dict [--list] [lang [lang [...]]] """ import argparse +import base64 +import json +import os import sys +import re +import urllib.parse +import urllib.request +import attr from qutebrowser.browser.webengine import spell +from qutebrowser.config import configdata + + +API_URL = 'https://chromium.googlesource.com/chromium/deps/hunspell_dictionaries.git/+/master/' + + +@attr.s +class Language: + """Dictionary language specs.""" + + code = attr.ib(None) + name = attr.ib(None) + file_basename = attr.ib(None) + file_extension = attr.ib('bdic') + + @property + def file_path(self): + return '.'.join([self.file_basename, self.file_extension]) def get_argparser(): @@ -46,18 +71,105 @@ def print_list(languages): print('{1}\t{0}'.format(lang.name, lang.code)) +def valid_languages(): + """Return a mapping from valid language codes to their names.""" + option = configdata.DATA['spellcheck.languages'] + return option.typ.valtype.valid_values.descriptions + + +def language_list_from_api(): + """Return a JSON with a list of available languages from Google API.""" + listurl = urllib.parse.urljoin(API_URL, '?format=JSON') + response = urllib.request.urlopen(listurl) + # TODO: what's up with the first 4 characters? + entries = json.loads(response.read().decode('utf-8')[4:])['entries'] + return entries + + +def available_languages(): + """Return a list of Language objects of all available languages.""" + lang_map = valid_languages() + api_list = language_list_from_api() + dict_re = re.compile(r""" + (?P(?P[a-z]{2}(-[A-Z]{2})?).*)\.bdic + """, re.VERBOSE) + code2file = {} + for lang in api_list: + match = dict_re.match(lang['name']) + if match is not None: + code2file[match.group('dict')] = match.group('filename') + return [ + Language(code, name, code2file[code]) + for code, name in lang_map.items() + ] + + +def download_dictionary(url, dest): + """Download a decoded dictionary file.""" + response = urllib.request.urlopen(url) + decoded = base64.decodebytes(response.read()) + with open(dest, 'bw') as dict_file: + dict_file.write(decoded) + + +def filter_languages(languages, selected): + """Filter a list of languages based on an inclusion list. + + Args: + languages: a list of languages to filter + selected: a list of keys to select + by: a function returning the selection key (code by default) + fail_on_unknown: whether to raise an error if there is an unknown + key in selected + """ + filtered_languages = [] + for language in languages: + if language.code in selected: + filtered_languages.append(language) + selected.remove(language.code) + if selected: + unknown = ', '.join(selected) + raise ValueError('unknown languages found: {}'.format(unknown)) + return filtered_languages + + +def install_lang(lang): + """Install a single lang given by the argument.""" + print('Installing {}: {}'.format(lang.code, lang.name)) + lang_url = urllib.parse.urljoin(API_URL, lang.file_path, '?format=TEXT') + if not os.path.isdir(spell.dictionary_dir()): + warn_msg = 'WARN: {} does not exist, creating the directory' + print(warn_msg.format(spell.dictionary_dir())) + os.makedirs(spell.dictionary_dir()) + print('Downloading {}'.format(lang_url)) + dest = os.path.join(spell.dictionary_dir(), lang.file_path) + download_dictionary(lang_url, dest) + print('Done.') + + +def install(languages): + """Install languages.""" + for lang in languages: + try: + install_lang(lang) + except PermissionError as e: + print(e) + + def main(): + if configdata.DATA is None: + configdata.init() parser = get_argparser() argv = sys.argv[1:] args = parser.parse_args(argv) - languages = spell.get_available_languages() + languages = available_languages() if args.list: print_list(languages) elif not args.languages: parser.print_usage() else: try: - spell.install(spell.filter_languages(languages, args.languages)) + install(filter_languages(languages, args.languages)) except ValueError as e: print(e) diff --git a/tests/end2end/conftest.py b/tests/end2end/conftest.py index 432905ff7..0b5b85505 100644 --- a/tests/end2end/conftest.py +++ b/tests/end2end/conftest.py @@ -29,7 +29,7 @@ import pstats import os.path import operator -from qutebrowser.browser.webengine.spell import get_installed_languages +from qutebrowser.browser.webengine import spell import pytest from PyQt5.QtCore import PYQT_VERSION @@ -120,18 +120,17 @@ def _get_backend_tag(tag): def _get_dictionary_tag(tag): """Handle tags like must_have_dict=en-US for BDD tests.""" - version_re = re.compile(r""" + dict_re = re.compile(r""" (?Pmust_have_dict|cannot_have_dict)=(?P[a-z]{2}-[A-Z]{2}) """, re.VERBOSE) - match = version_re.match(tag) + match = dict_re.match(tag) if not match: - #return pytest.mark.skip return None event = match.group('event') dictionary = match.group('dict') - has_dict = dictionary in [lang.code for lang in get_installed_languages()] + has_dict = spell.installed_file(dictionary) is not None if event == 'must_have_dict': return pytest.mark.skipif(not has_dict, reason=tag) elif event == 'cannot_have_dict': diff --git a/tests/end2end/features/spell.feature b/tests/end2end/features/spell.feature index 8fb996ec3..9465951c9 100644 --- a/tests/end2end/features/spell.feature +++ b/tests/end2end/features/spell.feature @@ -2,37 +2,29 @@ Feature: Setting spell checking for QtWebEngine - Background: - Given spell check languages are [] - @qtwebkit_skip @qt>=5.8 Scenario: Turn spell check on - Given spell check is off - When I run :set spell true - Then the option spell should be set to true - Then spell check is on + Given I set spellcheck.enabled to false + When I run :set spellcheck.enabled true + Then the option spellcheck.enabled should be set to true @qtwebkit_skip @qt>=5.8 Scenario: Turn spell check off - Given spell check is on - When I run :set spell false - Then the option spell should be set to false - Then spell check is off + Given I set spellcheck.enabled to true + When I run :set spellcheck.enabled false + Then the option spellcheck.enabled should be set to false @qtwebkit_skip @qt>=5.8 Scenario: Set an invalid language - When I run :set spell_languages ['invalid-language'] (invalid command) + When I run :set spellcheck.languages ['invalid-language'] (invalid command) Then the error "set: Invalid value 'invalid-language' *" should be shown - Then actual spell check languages are [] @qtwebkit_skip @qt>=5.8 @cannot_have_dict=af-ZA Scenario: Set valid but not installed language - When I run :set spell_languages ['af-ZA'] + When I run :set spellcheck.languages ['af-ZA'] Then the warning "Language af-ZA is not installed." should be shown - Then actual spell check languages are [] @qtwebkit_skip @qt>=5.8 @must_have_dict=en-US Scenario: Set valid and installed language - When I run :set spell_languages ["en-US"] - Then the option spell_languages should be set to ["en-US"] - Then actual spell check languages are ['en-US-7-1'] + When I run :set spellcheck.languages ["en-US"] + Then the option spellcheck.languages should be set to ["en-US"] diff --git a/tests/end2end/features/test_spell_bdd.py b/tests/end2end/features/test_spell_bdd.py index f6025e66b..58c3a3da8 100644 --- a/tests/end2end/features/test_spell_bdd.py +++ b/tests/end2end/features/test_spell_bdd.py @@ -20,39 +20,3 @@ import pytest_bdd as bdd bdd.scenarios('spell.feature') - - -@bdd.given(bdd.parsers.parse("spell check is {val}")) -def spellcheck_enabled_given(quteproc, val): - enabled = val == 'on' - quteproc.send_cmd(':debug-pyeval QWebEngineProfile.defaultProfile()' + - '.setSpellCheckEnabled({})'.format(enabled)) - quteproc.wait_for_load_finished('qute://pyeval') - - -@bdd.given(bdd.parsers.parse("spell check languages are {langs}")) -def spellcheck_langs_given(quteproc, langs): - quteproc.send_cmd(':debug-pyeval QWebEngineProfile.defaultProfile()' + - '.setSpellCheckLanguages({})'.format(langs)) - quteproc.wait_for_load_finished('qute://pyeval') - - -@bdd.then(bdd.parsers.parse("spell check is {val}")) -def spellcheck_enabled_then(quteproc, val): - quteproc.send_cmd(':debug-pyeval QWebEngineProfile.defaultProfile()' + - '.isSpellCheckEnabled()') - quteproc.wait_for_load_finished('qute://pyeval') - content = quteproc.get_content().strip() - if val == 'on': - assert content == 'True' - else: - assert content == 'False' - - -@bdd.then(bdd.parsers.parse("actual spell check languages are {langs}")) -def spellcheck_langs_then(quteproc, langs): - quteproc.send_cmd(':debug-pyeval QWebEngineProfile.defaultProfile()' + - '.spellCheckLanguages()') - quteproc.wait_for_load_finished('qute://pyeval') - actual_langs = quteproc.get_content().strip() - assert actual_langs == langs diff --git a/tests/unit/browser/webengine/test_spell.py b/tests/unit/browser/webengine/test_spell.py index 4a5496e20..2aebeffd5 100644 --- a/tests/unit/browser/webengine/test_spell.py +++ b/tests/unit/browser/webengine/test_spell.py @@ -18,78 +18,23 @@ # along with qutebrowser. If not, see . -from os.path import basename, join - -import pytest - from qutebrowser.browser.webengine import spell -AFRIKAANS = spell.Language('af-ZA', - 'Afrikaans (South Africa)', - 'af-ZA-3-0.bdic') -ENGLISH = spell.Language('en-US', - 'English (United States)', - 'en-US-7-1.bdic') -POLISH = spell.Language('pl-PL', - 'Polish (Poland)', - 'pl-PL-3-0.bdic') -LANGUAGE_LIST = [AFRIKAANS, ENGLISH, POLISH] +def test_installed_file_dictionary_does_not_exist(tmpdir, monkeypatch): + monkeypatch.setattr( + spell, 'dictionary_dir', lambda: '/some-non-existing-dir') + assert not spell.installed_file('en-US') -def test_get_installed_languages_empty(tmpdir, mocker): - mocker.patch('qutebrowser.browser.webengine.spell.get_dictionary_dir', - lambda: '/some-non-existing-dir') - assert spell.get_installed_languages() == [] +def test_installed_file_dictionary_not_installed(tmpdir, monkeypatch): + monkeypatch.setattr(spell, 'dictionary_dir', lambda: str(tmpdir)) + assert not spell.installed_file('en-US') -def test_get_installed_languages_non_empty(tmpdir, mocker): - mocker.patch('qutebrowser.browser.webengine.spell.get_dictionary_dir', - lambda: str(tmpdir)) - for lang in LANGUAGE_LIST: - open(join(str(tmpdir), lang.file), 'w', encoding='UTF-8').close() - for actual, expected in zip(spell.get_installed_languages(), - LANGUAGE_LIST): - assert (actual.code, actual.name, actual.file) ==\ - (expected.code, expected.name, expected.file) - - -def test_get_available_languages(): - language_list = spell.get_available_languages() - assert len(language_list) == 42 - first_lang = language_list[0] - assert (first_lang.code, first_lang.name, first_lang.file) ==\ - (AFRIKAANS.code, AFRIKAANS.name, AFRIKAANS.file) - - -def test_filter_languages(): - filtered_languages = spell.filter_languages(LANGUAGE_LIST, ['af-ZA']) - assert filtered_languages == [AFRIKAANS] - filtered_languages = spell.filter_languages(LANGUAGE_LIST, - ['pl-PL', 'en-US']) - assert filtered_languages == [ENGLISH, POLISH] - with pytest.raises(ValueError): - spell.filter_languages(LANGUAGE_LIST, ['pl-PL', 'en-GB']) - filtered_languages = spell.filter_languages(LANGUAGE_LIST, - ['pl-PL-3-0.bdic'], - by=lambda lang: lang.file) - assert filtered_languages == [POLISH] - - -def test_install(tmpdir, mocker): - mocker.patch('qutebrowser.browser.webengine.spell.get_dictionary_dir', - lambda: str(tmpdir)) - mocker.patch('qutebrowser.browser.webengine.spell.download_dictionary', - lambda url, dest: open(dest, 'w', encoding='UTF-8').close()) - spell.install(LANGUAGE_LIST) - installed_files = [basename(str(file)) for file in tmpdir.listdir()] - expected_files = [lang.file for lang in LANGUAGE_LIST] - assert sorted(installed_files) == sorted(expected_files) - - -# TODO: move somewhere to be checked before a release -#def test_available_langs(): -# for lang in spell.get_available_languages(): -# lang_url = urljoin(spell.repository_url, lang.file) -# response = head(lang_url) -# assert response.status_code == 302 +def test_installed_file_dictionary_installed(tmpdir, monkeypatch): + monkeypatch.setattr(spell, 'dictionary_dir', lambda: str(tmpdir)) + for lang_file in ['en-US-7-1.bdic', 'pl-PL-3-0.bdic']: + (tmpdir / lang_file).ensure() + assert spell.installed_file('en-US') == 'en-US-7-1' + assert spell.installed_file('pl-PL') == 'pl-PL-3-0' diff --git a/tests/unit/scripts/test_install_dict.py b/tests/unit/scripts/test_install_dict.py new file mode 100644 index 000000000..da96aacdd --- /dev/null +++ b/tests/unit/scripts/test_install_dict.py @@ -0,0 +1,71 @@ +# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: + +# Copyright 2017 Michal Siedlaczek + +# This file is part of qutebrowser. +# +# qutebrowser is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# qutebrowser is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with qutebrowser. If not, see . + + +import py.path # pylint: disable=no-name-in-module +import pytest + +from qutebrowser.browser.webengine import spell +from scripts import install_dict +from qutebrowser.config import configdata + +AFRIKAANS = install_dict.Language( + 'af-ZA', + 'Afrikaans (South Africa)', + 'af-ZA-3-0') +ENGLISH = install_dict.Language( + 'en-US', + 'English (United States)', + 'en-US-7-1') +POLISH = install_dict.Language( + 'pl-PL', + 'Polish (Poland)', + 'pl-PL-3-0') + +LANGUAGE_LIST = [AFRIKAANS, ENGLISH, POLISH] + + +@pytest.fixture(autouse=True) +def configdata_init(): + """Initialize configdata if needed.""" + if configdata.DATA is None: + configdata.init() + + +def test_filter_languages(): + filtered_langs = install_dict.filter_languages(LANGUAGE_LIST, ['af-ZA']) + assert filtered_langs == [AFRIKAANS] + + filtered_langs = install_dict.filter_languages( + LANGUAGE_LIST, ['pl-PL', 'en-US']) + assert filtered_langs == [ENGLISH, POLISH] + + with pytest.raises(ValueError): + install_dict.filter_languages(LANGUAGE_LIST, ['pl-PL', 'en-GB']) + + +def test_install(tmpdir, monkeypatch): + monkeypatch.setattr(spell, 'dictionary_dir', lambda: str(tmpdir)) + monkeypatch.setattr( + install_dict, 'download_dictionary', + lambda _url, dest: py.path.local(dest).ensure()) # pylint: disable=no-member + install_dict.install(LANGUAGE_LIST) + installed_files = [f.basename for f in tmpdir.listdir()] + expected_files = [lang.file_path for lang in LANGUAGE_LIST] + assert sorted(installed_files) == sorted(expected_files)