287 lines
12 KiB
Python
287 lines
12 KiB
Python
|
#
|
||
|
# Copyright (C) 2013 Red Hat, Inc.
|
||
|
#
|
||
|
# This copyrighted material is made available to anyone wishing to use,
|
||
|
# modify, copy, or redistribute it subject to the terms and conditions of
|
||
|
# the GNU General Public License v.2, or (at your option) any later version.
|
||
|
# This program is distributed in the hope that it will be useful, but WITHOUT
|
||
|
# ANY WARRANTY expressed or implied, including the implied warranties 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 this program; if not, write to the
|
||
|
# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||
|
# 02110-1301, USA. Any Red Hat trademarks that are incorporated in the
|
||
|
# source code or documentation are not subject to the GNU General Public
|
||
|
# License and may only be used or replicated with the express permission of
|
||
|
# Red Hat, Inc.
|
||
|
#
|
||
|
import locale as locale_mod
|
||
|
import unittest
|
||
|
import pytest
|
||
|
|
||
|
from unittest.mock import call, patch, MagicMock
|
||
|
from io import StringIO
|
||
|
|
||
|
from pyanaconda import localization
|
||
|
from pyanaconda.core.constants import DEFAULT_LANG
|
||
|
from pyanaconda.core.util import execWithCapture
|
||
|
|
||
|
# pylint: disable=environment-modify
|
||
|
# required due to mocking os.environ
|
||
|
|
||
|
class LangcodeLocaleParsingTests(unittest.TestCase):
|
||
|
|
||
|
def tearDown(self):
|
||
|
locale_mod.setlocale(locale_mod.LC_ALL, DEFAULT_LANG)
|
||
|
|
||
|
def test_is_valid(self):
|
||
|
|
||
|
assert localization.is_valid_langcode("en")
|
||
|
assert localization.is_valid_langcode("eo")
|
||
|
assert localization.is_valid_langcode("en_US")
|
||
|
assert localization.is_valid_langcode("csb")
|
||
|
assert localization.is_valid_langcode("cs_CZ.UTF-8@latin")
|
||
|
assert localization.is_valid_langcode("ca_ES.UTF-8@valencia")
|
||
|
|
||
|
assert localization.is_valid_langcode("cs_JA.iso8859-1@cyrl")
|
||
|
# nonsensical but still valid - parses correctly and has a language name
|
||
|
|
||
|
assert not localization.is_valid_langcode("blah")
|
||
|
assert not localization.is_valid_langcode("")
|
||
|
assert not localization.is_valid_langcode(None)
|
||
|
|
||
|
def test_invalid_raise_decorator(self):
|
||
|
with pytest.raises(localization.InvalidLocaleSpec):
|
||
|
localization.get_native_name("blah")
|
||
|
with pytest.raises(localization.InvalidLocaleSpec):
|
||
|
localization.is_supported_locale("blah")
|
||
|
|
||
|
def test_is_supported(self):
|
||
|
assert localization.is_supported_locale("en")
|
||
|
assert localization.is_supported_locale("en_US")
|
||
|
assert localization.locale_supported_in_console("en")
|
||
|
assert localization.locale_supported_in_console("en_US")
|
||
|
|
||
|
def test_native_name(self):
|
||
|
assert localization.get_native_name("de") == "Deutsch"
|
||
|
assert localization.get_native_name("cs_CZ") == "Čeština (Česko)"
|
||
|
|
||
|
def test_english_name(self):
|
||
|
assert localization.get_english_name("de") == "German"
|
||
|
assert localization.get_english_name("cs_CZ") == "Czech (Czechia)"
|
||
|
|
||
|
def test_available_translations(self):
|
||
|
assert "en" in localization.get_available_translations()
|
||
|
|
||
|
def test_territory_locales(self):
|
||
|
assert "en_US.UTF-8" in localization.get_territory_locales("US")
|
||
|
assert "en_GB.UTF-8" in localization.get_territory_locales("GB")
|
||
|
|
||
|
def test_locale_keyboards(self):
|
||
|
assert localization.get_locale_keyboards("en_US") == ["us"]
|
||
|
assert localization.get_locale_keyboards("en_GB") == ["gb"]
|
||
|
|
||
|
def test_common_keyboard_layouts(self):
|
||
|
layouts = localization.get_common_keyboard_layouts()
|
||
|
assert "us" in layouts
|
||
|
assert "fr(oss)" in layouts
|
||
|
assert "de(nodeadkeys)" in layouts
|
||
|
|
||
|
def test_locale_timezones(self):
|
||
|
assert "Europe/Oslo" in localization.get_locale_timezones("no")
|
||
|
|
||
|
@patch.dict("pyanaconda.localization.os.environ", dict())
|
||
|
def test_xlated_tz(self):
|
||
|
localization.os.environ["LANG"] = "en_US"
|
||
|
assert "Europe/Barcelona" == localization.get_xlated_timezone("Europe/Barcelona")
|
||
|
localization.os.environ["LANG"] = "cs_CZ"
|
||
|
assert "Evropa/Praha" == localization.get_xlated_timezone("Europe/Prague")
|
||
|
localization.os.environ["LANG"] = "blah"
|
||
|
with pytest.raises(localization.InvalidLocaleSpec):
|
||
|
localization.get_xlated_timezone("America/New_York")
|
||
|
|
||
|
|
||
|
class SetupLocaleTest(unittest.TestCase):
|
||
|
|
||
|
def tearDown(self):
|
||
|
locale_mod.setlocale(locale_mod.LC_ALL, DEFAULT_LANG)
|
||
|
|
||
|
@patch("pyanaconda.localization.setenv")
|
||
|
@patch("pyanaconda.localization.locale_mod.setlocale")
|
||
|
@patch("pyanaconda.localization.set_modules_locale")
|
||
|
def test_setup_locale_notext(self, set_modules_locale_mock, setlocale_mock, setenv_mock):
|
||
|
"""Test setup_locale in GUI mode"""
|
||
|
|
||
|
loc_proxy = MagicMock()
|
||
|
|
||
|
locale = localization.setup_locale("sk", localization_proxy=loc_proxy)
|
||
|
|
||
|
assert loc_proxy.Language == "sk"
|
||
|
setenv_mock.assert_called_once_with("LANG", "sk")
|
||
|
setlocale_mock.assert_called_once_with(locale_mod.LC_ALL, "sk")
|
||
|
set_modules_locale_mock.assert_called_once_with("sk")
|
||
|
|
||
|
assert locale == "sk"
|
||
|
|
||
|
@patch.dict("pyanaconda.localization.os.environ", dict())
|
||
|
@patch("pyanaconda.localization.locale_supported_in_console", return_value=False)
|
||
|
@patch("pyanaconda.localization.setenv")
|
||
|
@patch("pyanaconda.localization.locale_mod.setlocale")
|
||
|
@patch("pyanaconda.localization.set_modules_locale")
|
||
|
def test_setup_locale_text(self, set_modules_locale_mock, setlocale_mock, setenv_mock,
|
||
|
locale_supported_in_console_mock):
|
||
|
"""Test setup_locale in TUI mode"""
|
||
|
# note: to eliminate unpredictable support in console, mocking such that it always fails
|
||
|
|
||
|
locale = localization.setup_locale("ja_JP", text_mode=True)
|
||
|
|
||
|
locale_supported_in_console_mock.assert_called_once_with("ja_JP")
|
||
|
assert localization.os.environ["LANG"] == DEFAULT_LANG
|
||
|
setenv_mock.assert_called_once_with("LANG", DEFAULT_LANG)
|
||
|
setlocale_mock.assert_called_once_with(locale_mod.LC_ALL, DEFAULT_LANG)
|
||
|
set_modules_locale_mock.assert_called_once_with(DEFAULT_LANG)
|
||
|
|
||
|
assert locale == DEFAULT_LANG
|
||
|
|
||
|
@patch("pyanaconda.localization.setenv")
|
||
|
@patch("pyanaconda.localization.locale_mod.setlocale", side_effect=[locale_mod.Error, None])
|
||
|
@patch("pyanaconda.localization.set_modules_locale")
|
||
|
def test_setup_locale_setlocale_fail(self, set_modules_locale_mock, setlocale_mock, setenv_mock):
|
||
|
"""Test setup_locale with failure in setlocale"""
|
||
|
|
||
|
locale = localization.setup_locale("es_ES")
|
||
|
|
||
|
setenv_mock.assert_has_calls([
|
||
|
call("LANG", "es_ES"),
|
||
|
call("LANG", DEFAULT_LANG)
|
||
|
])
|
||
|
setlocale_mock.assert_has_calls([
|
||
|
call(locale_mod.LC_ALL, "es_ES"),
|
||
|
call(locale_mod.LC_ALL, DEFAULT_LANG)
|
||
|
])
|
||
|
setlocale_mock.assert_called_with(locale_mod.LC_ALL, DEFAULT_LANG)
|
||
|
set_modules_locale_mock.assert_called_once_with(DEFAULT_LANG)
|
||
|
|
||
|
assert locale == DEFAULT_LANG
|
||
|
|
||
|
|
||
|
class SetupLocaleEnvironmentTest(unittest.TestCase):
|
||
|
|
||
|
@patch("pyanaconda.localization.get_language_locales")
|
||
|
@patch.dict("pyanaconda.localization.os.environ",
|
||
|
{"LANGUAGE": "de", "LANG": "de", "LC_ALL": "de", "LC_MESSAGES": "de"})
|
||
|
def test_setup_locale_environment_param_ok(self, locales_mock):
|
||
|
"""Test setup_locale_environment() with parameter"""
|
||
|
# success case
|
||
|
locales_mock.return_value = ["fr_FR.UTF-8"]
|
||
|
|
||
|
localization.setup_locale_environment("fr")
|
||
|
|
||
|
assert "fr_FR.UTF-8" == localization.os.environ["LANG"]
|
||
|
assert "LANGUAGE" not in localization.os.environ
|
||
|
assert "LC_MESSAGES" not in localization.os.environ
|
||
|
assert "LC_ALL" not in localization.os.environ
|
||
|
|
||
|
# mock a failure
|
||
|
locales_mock.return_value = []
|
||
|
locales_mock.side_effect = localization.InvalidLocaleSpec
|
||
|
|
||
|
localization.setup_locale_environment("iu")
|
||
|
|
||
|
assert DEFAULT_LANG in localization.os.environ["LANG"]
|
||
|
|
||
|
@patch.dict("pyanaconda.localization.os.environ", dict())
|
||
|
def test_setup_locale_environment_vars(self):
|
||
|
"""Test setup_locale_environment() with multiple environment variables"""
|
||
|
for varname in ("LANGUAGE", "LC_ALL", "LC_MESSAGES", "LANG"):
|
||
|
|
||
|
localization.os.environ.clear()
|
||
|
localization.os.environ[varname] = "ko"
|
||
|
|
||
|
localization.setup_locale_environment(None)
|
||
|
|
||
|
assert "ko_KR.UTF-8" == localization.os.environ["LANG"]
|
||
|
if varname != "LANG":
|
||
|
assert varname not in localization.os.environ
|
||
|
|
||
|
@patch.dict("pyanaconda.localization.os.environ", {"LANG": "blah"})
|
||
|
def test_setup_locale_environment_vars_invalid(self):
|
||
|
"""Test setup_locale_environment() with invalid environment variable input"""
|
||
|
localization.setup_locale_environment(None)
|
||
|
|
||
|
assert DEFAULT_LANG == localization.os.environ["LANG"]
|
||
|
|
||
|
@patch("pyanaconda.localization.open")
|
||
|
@patch.dict("pyanaconda.localization.os.environ", dict())
|
||
|
def test_setup_locale_environment_fallback_efi_ok(self, open_mock):
|
||
|
"""Test setup_locale_environment() fallback to EFI vars"""
|
||
|
# success with valid data
|
||
|
# first 4 bytes binary attributes, then language with - instead of _, minimum 10 bytes
|
||
|
open_mock.return_value = StringIO("\x07\x00\x00\x00de-DE\x00")
|
||
|
localization.os.environ.clear()
|
||
|
|
||
|
localization.setup_locale_environment(None)
|
||
|
|
||
|
assert open_mock.called
|
||
|
assert "de" in localization.os.environ["LANG"]
|
||
|
|
||
|
@patch("pyanaconda.localization.open")
|
||
|
@patch.dict("pyanaconda.localization.os.environ", dict())
|
||
|
def test_setup_locale_environment_fallback_efi_bad(self, open_mock):
|
||
|
"""Test setup_locale_environment() fallback to EFI vars with bad contents"""
|
||
|
# failure with invalid data - too short
|
||
|
open_mock.return_value = StringIO("\x00")
|
||
|
|
||
|
localization.setup_locale_environment(None)
|
||
|
|
||
|
assert DEFAULT_LANG == localization.os.environ["LANG"]
|
||
|
|
||
|
|
||
|
class LangcodeLocaleMatchingTests(unittest.TestCase):
|
||
|
|
||
|
def tearDown(self):
|
||
|
locale_mod.setlocale(locale_mod.LC_ALL, DEFAULT_LANG)
|
||
|
|
||
|
def test_find_best_locale_match(self):
|
||
|
"""Finding best locale matches should work as expected."""
|
||
|
# can find best matches
|
||
|
assert localization.find_best_locale_match("cs_CZ", ["cs", "cs_CZ", "en", "en_US"]) == "cs_CZ"
|
||
|
assert localization.find_best_locale_match("cs", ["cs_CZ", "cs", "en", "en_US"]) == "cs"
|
||
|
assert localization.find_best_locale_match("pt_BR", ["pt", "pt_BR"]) == "pt_BR"
|
||
|
assert localization.find_best_locale_match("pt_BR", ["pt", "pt_BR", "pt_PT"]) == "pt_BR"
|
||
|
assert localization.find_best_locale_match("cs_CZ.UTF-8", ["cs", "cs_CZ", "cs_CZ.UTF-8"]) == \
|
||
|
"cs_CZ.UTF-8"
|
||
|
assert localization.find_best_locale_match("cs_CZ.UTF-8@latin",
|
||
|
["cs", "cs_CZ@latin", "cs_CZ.UTF-8"]) == "cs_CZ@latin"
|
||
|
|
||
|
# no matches
|
||
|
assert localization.find_best_locale_match("pt_BR", ["en_BR", "en"]) is None
|
||
|
assert localization.find_best_locale_match("cs_CZ.UTF-8", ["en", "en.UTF-8"]) is None
|
||
|
|
||
|
# nonsense
|
||
|
assert localization.find_best_locale_match("ja", ["blah"]) is None
|
||
|
assert localization.find_best_locale_match("blah", ["en_US.UTF-8"]) is None
|
||
|
|
||
|
def test_find_best_locale_match_posix(self):
|
||
|
"""Finding best POSIX matches should work as expected."""
|
||
|
match = localization.find_best_locale_match("C", ["C.UTF-8"])
|
||
|
assert match == "C.UTF-8"
|
||
|
|
||
|
match = localization.find_best_locale_match("C.UTF-8", ["en_US"])
|
||
|
assert match == "en_US"
|
||
|
|
||
|
match = localization.find_best_locale_match("en_US", ["C.UTF-8"])
|
||
|
assert match is None
|
||
|
|
||
|
match = localization.find_best_locale_match("cs_CZ", ["C.UTF-8"])
|
||
|
assert match is None
|
||
|
|
||
|
def test_resolve_date_format(self):
|
||
|
"""All locales' date formats should be properly resolved."""
|
||
|
locales = (line.strip() for line in execWithCapture("locale", ["-a"]).splitlines())
|
||
|
for locale in locales:
|
||
|
locale_mod.setlocale(locale_mod.LC_ALL, locale)
|
||
|
order = localization.resolve_date_format(1, 2, 3, fail_safe=False)[0]
|
||
|
for i in (1, 2, 3):
|
||
|
assert i in order
|