diff --git a/tests/common.py b/tests/common.py index f07e9b469..17d48171c 100644 --- a/tests/common.py +++ b/tests/common.py @@ -17,13 +17,14 @@ import json import re import time +import typing as t from pathlib import Path from typing import TYPE_CHECKING, Generator, Optional from unittest import mock import pytest -from trezorlib import btc, messages, tools +from trezorlib import btc, cosi, messages, tools from trezorlib.messages import ButtonRequestType if TYPE_CHECKING: @@ -33,6 +34,7 @@ if TYPE_CHECKING: from trezorlib.debuglink import TrezorClientDebugLink as Client from trezorlib.messages import ButtonRequest +PRIVATE_KEYS_DEV = [byte * 32 for byte in (b"\xdd", b"\xde", b"\xdf")] BRGeneratorType = Generator[None, messages.ButtonRequest, None] @@ -291,3 +293,19 @@ def compact_size(n: int) -> bytes: return bytes([254]) + n.to_bytes(4, "little") else: return bytes([255]) + n.to_bytes(8, "little") + + +def sign_with_privkeys(digest: bytes, privkeys: t.Sequence[bytes]) -> bytes: + """Locally produce a CoSi signature.""" + pubkeys = [cosi.pubkey_from_privkey(sk) for sk in privkeys] + nonces = [cosi.get_nonce(sk, digest, i) for i, sk in enumerate(privkeys)] + + global_pk = cosi.combine_keys(pubkeys) + global_R = cosi.combine_keys(R for _, R in nonces) + + sigs = [ + cosi.sign_with_privkey(digest, sk, global_pk, r, global_R) + for sk, (r, _) in zip(privkeys, nonces) + ] + + return cosi.combine_sig(global_R, sigs) diff --git a/tests/conftest.py b/tests/conftest.py index 5216db44f..b2a353ca9 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -23,13 +23,18 @@ from typing import TYPE_CHECKING, Generator, Iterator import pytest import xdist -from trezorlib import debuglink, log, translations +from trezorlib import debuglink, log +from trezorlib._internal import translations from trezorlib.debuglink import TrezorClientDebugLink as Client -from trezorlib.device import apply_settings, change_language +from trezorlib.device import apply_settings from trezorlib.device import wipe as wipe_device from trezorlib.transport import enumerate_devices, get_transport -from . import ui_tests +# register rewrites before importing from local package +# so that we see details of failed asserts from this module +pytest.register_assert_rewrite("tests.common") + +from . import translations, ui_tests from .device_handler import BackgroundDeviceHandler from .emulators import EmulatorWrapper @@ -43,14 +48,6 @@ if TYPE_CHECKING: HERE = Path(__file__).resolve().parent CORE = HERE.parent / "core" -TRANSLATIONS = CORE / "embed" / "rust" / "src" / "ui" / "translations" - -CS_JSON = TRANSLATIONS / "cs.json" -FR_JSON = TRANSLATIONS / "fr.json" - - -# So that we see details of failed asserts from this module -pytest.register_assert_rewrite("tests.common") def _emulator_wrapper_main_args() -> list[str]: @@ -146,26 +143,8 @@ def _raw_client(request: pytest.FixtureRequest) -> Client: # Not doing it for T1 if client.features.model != "1": lang = request.session.config.getoption("lang") or "en" - _set_language(client, lang) # type: ignore - - return client - - -def _set_language(client: Client, lang: str) -> Client: - model = client.features.model or "" - if lang == "en": - with client: - change_language(client, language_data=b"") - elif lang == "cs": - change_language( - client, language_data=translations.blob_from_file(CS_JSON, model) - ) - elif lang == "fr": - change_language( - client, language_data=translations.blob_from_file(FR_JSON, model) - ) - else: - raise RuntimeError(f"Unknown language: {lang}") + assert isinstance(lang, str) + translations.set_language(client, lang) return client diff --git a/tests/device_tests/ethereum/common.py b/tests/device_tests/ethereum/common.py index ad04df978..5f37c18d2 100644 --- a/tests/device_tests/ethereum/common.py +++ b/tests/device_tests/ethereum/common.py @@ -4,25 +4,9 @@ import io import typing as t from hashlib import sha256 -from trezorlib import cosi, definitions, messages, protobuf +from trezorlib import definitions, messages, protobuf -PRIVATE_KEYS_DEV = [byte * 32 for byte in (b"\xdd", b"\xde", b"\xdf")] - - -def sign_with_privkeys(digest: bytes, privkeys: t.Sequence[bytes]) -> bytes: - """Locally produce a CoSi signature.""" - pubkeys = [cosi.pubkey_from_privkey(sk) for sk in privkeys] - nonces = [cosi.get_nonce(sk, digest, i) for i, sk in enumerate(privkeys)] - - global_pk = cosi.combine_keys(pubkeys) - global_R = cosi.combine_keys(R for _, R in nonces) - - sigs = [ - cosi.sign_with_privkey(digest, sk, global_pk, r, global_R) - for sk, (r, _) in zip(privkeys, nonces) - ] - - return cosi.combine_sig(global_R, sigs) +from ...common import sign_with_privkeys, PRIVATE_KEYS_DEV def make_network( diff --git a/tests/device_tests/test_language.py b/tests/device_tests/test_language.py index 3b8d2bdcd..6103b5a8c 100644 --- a/tests/device_tests/test_language.py +++ b/tests/device_tests/test_language.py @@ -14,85 +14,41 @@ # You should have received a copy of the License along with this library. # If not, see . -import json -from contextlib import contextmanager from copy import deepcopy -from pathlib import Path -from typing import Any, Dict, Generator +from typing import Iterator import pytest -from trezorlib import debuglink, device, exceptions, messages, translations +from trezorlib import debuglink, device, exceptions, messages, models +from trezorlib._internal import translations from trezorlib.debuglink import TrezorClientDebugLink as Client -pytestmark = pytest.mark.skip_t1 - -HERE = Path(__file__).parent.resolve() -CORE = HERE.parent.parent / "core" -TRANSLATIONS = CORE / "embed" / "rust" / "src" / "ui" / "translations" -FONTS = TRANSLATIONS / "fonts" -ORDER_FILE = TRANSLATIONS / "order.json" +from ..translations import ( + LANGUAGES, + get_lang_json, + set_language, + build_and_sign_blob, +) -EN_JSON = TRANSLATIONS / "en.json" -CS_JSON = TRANSLATIONS / "cs.json" -FR_JSON = TRANSLATIONS / "fr.json" - -MAX_DATA_LENGTH = {"T": 48 * 1024, "Safe 3": 32 * 1024} +pytestmark = pytest.mark.skip_t1 -def _read_confirm_word(file: Path) -> str: - content = json.loads(file.read_text()) - return content["translations"]["words"]["confirm"] +MAX_DATA_LENGTH = {models.T2T1: 48 * 1024, models.T2B1: 32 * 1024} -ENGLISH_CONFIRM = _read_confirm_word(EN_JSON) -CZECH_CONFIRM = _read_confirm_word(CS_JSON) -FRENCH_CONFIRM = _read_confirm_word(FR_JSON) +def get_confirm(lang: str) -> str: + content = get_lang_json(lang) + return content["translations"]["words__confirm"] -@contextmanager -def _set_english_return_back(client: Client) -> Generator[Client, None, None]: +@pytest.fixture +def client(client: Client) -> Iterator[Client]: lang_before = client.features.language or "" try: - _set_default_english(client) + set_language(client, "en") yield client finally: - if lang_before.startswith("en"): - _set_default_english(client) - elif lang_before == "cs": - _set_full_czech(client) - elif lang_before == "fr": - _set_full_french(client) - else: - raise RuntimeError(f"Unknown language: {lang_before}") - - -def _set_full_czech(client: Client): - device.change_language( - client, - language_data=translations.blob_from_file(CS_JSON, client.features.model or ""), - ) - - -def _set_full_french(client: Client): - device.change_language( - client, - language_data=translations.blob_from_file(FR_JSON, client.features.model or ""), - ) - - -def _set_default_english(client: Client): - with client: - device.change_language(client, language_data=b"") - - -def _get_data_from_dict(data: Dict[str, Any], client: Client) -> bytes: - return translations.blob_from_dict( - data, - font_dir=FONTS, - order_json_file=ORDER_FILE, - model=client.features.model or "", - ) + set_language(client, lang_before[:2]) def _check_ping_screen_texts(client: Client, title: str, right_button: str) -> None: @@ -115,210 +71,186 @@ def _check_ping_screen_texts(client: Client, title: str, right_button: str) -> N def test_change_language_errors(client: Client): - with _set_english_return_back(client) as client: - assert client.features.language == "en-US" - - # Translations too short - # Sending less data than the header length - with pytest.raises( - exceptions.TrezorFailure, match="Translations too short" - ), client: - bad_data = (translations.HEADER_LEN - 1) * b"a" - device.change_language(client, language_data=bad_data) - assert client.features.language == "en-US" - - # Translations too long - # Sending more than allowed by the flash capacity - max_length = MAX_DATA_LENGTH[client.features.model] - with pytest.raises( - exceptions.TrezorFailure, match="Translations too long" - ), client: - bad_data = (max_length + 1) * b"a" - device.change_language(client, language_data=bad_data) - assert client.features.language == "en-US" - - # Invalid header data length - # Sending more data than advertised in the header - with pytest.raises( - exceptions.TrezorFailure, match="Invalid header data length" - ), client: - good_data = translations.blob_from_file( - CS_JSON, client.features.model or "" - ) - bad_data = good_data + b"abcd" - device.change_language(client, language_data=bad_data) - assert client.features.language == "en-US" - - # Invalid header magic - # Does not match the expected magic - with pytest.raises( - exceptions.TrezorFailure, match="Invalid header magic" - ), client: - good_data = translations.blob_from_file( - CS_JSON, client.features.model or "" - ) - bad_data = 4 * b"a" + good_data[4:] - device.change_language(client, language_data=bad_data) - assert client.features.language == "en-US" - - # Invalid header data - # Putting non-zero bytes where zero is expected - with pytest.raises( - exceptions.TrezorFailure, match="Invalid header data" - ), client: - good_data = translations.blob_from_file( - CS_JSON, client.features.model or "" - ) - pre_sig_pos = translations.HEADER_LEN - translations.SIG_LEN - bad_data = good_data[: pre_sig_pos - 4] + 4 * b"a" + good_data[pre_sig_pos:] - device.change_language( - client, - language_data=bad_data, - ) - assert client.features.language == "en-US" - - # Invalid data hash - # Changing the data after their hash has been calculated - with pytest.raises(exceptions.TrezorFailure, match="Invalid data hash"), client: - good_data = translations.blob_from_file( - CS_JSON, client.features.model or "" - ) - bad_data = good_data[:-8] + 8 * b"a" - device.change_language( - client, - language_data=bad_data, - ) - assert client.features.language == "en-US" - - # Invalid translations version - # Change the version to one not matching the current device - with pytest.raises( - exceptions.TrezorFailure, match="Invalid translations version" - ), client: - with open(CS_JSON, "r") as f: - data = json.load(f) - data["header"]["version"] = "3.5.4" - device.change_language( - client, - language_data=_get_data_from_dict(data, client), - ) - assert client.features.language == "en-US" - - # Invalid header version - # Version is not a valid semver with integers - with pytest.raises( - exceptions.TrezorFailure, match="Invalid header version" - ), client: - with open(CS_JSON, "r") as f: - data = json.load(f) - data["header"]["version"] = "ABC.XYZ.DEF" - device.change_language( - client, - language_data=_get_data_from_dict(data, client), - ) - assert client.features.language == "en-US" - - # Invalid translations signature - # Modifying the signature part of the header - with pytest.raises( - exceptions.TrezorFailure, match="Invalid translations signature" - ), client: - good_data = translations.blob_from_file( - CS_JSON, client.features.model or "" - ) - bad_data = ( - good_data[: translations.HEADER_LEN - 8] - + 8 * b"a" - + good_data[translations.HEADER_LEN :] - ) - device.change_language( - client, - language_data=bad_data, - ) - assert client.features.language == "en-US" - - _check_ping_screen_texts(client, ENGLISH_CONFIRM, ENGLISH_CONFIRM) - - -def test_full_language_change(client: Client): - with _set_english_return_back(client) as client: - assert client.features.language == "en-US" - - # Setting cs language - _set_full_czech(client) - assert client.features.language == "cs" - _check_ping_screen_texts(client, CZECH_CONFIRM, CZECH_CONFIRM) - - # Setting fr language - _set_full_french(client) - assert client.features.language == "fr" - _check_ping_screen_texts(client, FRENCH_CONFIRM, FRENCH_CONFIRM) - - # Setting the default language via empty data - _set_default_english(client) - assert client.features.language == "en-US" - _check_ping_screen_texts(client, ENGLISH_CONFIRM, ENGLISH_CONFIRM) + assert client.features.language == "enUS" + + # Translations too short + # Sending less data than the header length + with pytest.raises( + exceptions.TrezorFailure, match="Translations too short" + ), client: + bad_data = (translations.HEADER_LEN - 1) * b"a" + device.change_language(client, language_data=bad_data) + assert client.features.language == "enUS" + + # Translations too long + # Sending more than allowed by the flash capacity + max_length = MAX_DATA_LENGTH[client.model] + with pytest.raises(exceptions.TrezorFailure, match="Translations too long"), client: + bad_data = (max_length + 1) * b"a" + device.change_language(client, language_data=bad_data) + assert client.features.language == "enUS" + + # Invalid header data length + # Sending more data than advertised in the header + with pytest.raises( + exceptions.TrezorFailure, match="Invalid header data length" + ), client: + good_data = build_and_sign_blob("cs", client.model) + bad_data = good_data + b"abcd" + device.change_language(client, language_data=bad_data) + assert client.features.language == "enUS" + + # Invalid header magic + # Does not match the expected magic + with pytest.raises(exceptions.TrezorFailure, match="Invalid header magic"), client: + good_data = build_and_sign_blob("cs", client.model) + bad_data = 4 * b"a" + good_data[4:] + device.change_language(client, language_data=bad_data) + assert client.features.language == "enUS" + + # Invalid header data + # Putting non-zero bytes where zero is expected + with pytest.raises(exceptions.TrezorFailure, match="Invalid header data"), client: + good_data = build_and_sign_blob("cs", client.model) + pre_sig_pos = translations.HEADER_LEN - translations.SIG_LEN + bad_data = good_data[: pre_sig_pos - 4] + 4 * b"a" + good_data[pre_sig_pos:] + device.change_language( + client, + language_data=bad_data, + ) + assert client.features.language == "enUS" + + # Invalid data hash + # Changing the data after their hash has been calculated + with pytest.raises(exceptions.TrezorFailure, match="Invalid data hash"), client: + good_data = build_and_sign_blob("cs", client.model) + bad_data = good_data[:-8] + 8 * b"a" + device.change_language( + client, + language_data=bad_data, + ) + assert client.features.language == "enUS" + + # Invalid translations version + # Change the version to one not matching the current device + with pytest.raises( + exceptions.TrezorFailure, match="Invalid translations version" + ), client: + data = get_lang_json("cs") + data["header"]["version"] = "3.5.4" + device.change_language( + client, + language_data=build_and_sign_blob(data, client.model), + ) + assert client.features.language == "enUS" + + # Invalid header version + # Version is not a valid semver with integers + with pytest.raises( + exceptions.TrezorFailure, match="Invalid header version" + ), client: + data = get_lang_json("cs") + data["header"]["version"] = "ABC.XYZ.DEF" + device.change_language( + client, + language_data=build_and_sign_blob(data, client.model), + ) + assert client.features.language == "enUS" + + # Invalid translations signature + # Modifying the signature part of the header + with pytest.raises( + exceptions.TrezorFailure, match="Invalid translations signature" + ), client: + good_data = translations.blob_from_file( + get_lang_json("cs"), client.features.model or "" + ) + bad_data = ( + good_data[: translations.HEADER_LEN - 8] + + 8 * b"a" + + good_data[translations.HEADER_LEN :] + ) + device.change_language( + client, + language_data=bad_data, + ) + assert client.features.language == "enUS" + + _check_ping_screen_texts(client, get_confirm("en"), get_confirm("en")) + + +@pytest.mark.parametrize("lang", LANGUAGES) +def test_full_language_change(client: Client, lang: str): + assert client.features.language == "enUS" + + # Setting selected language + set_language(client, lang) + assert client.features.language[:2] == lang + _check_ping_screen_texts(client, get_confirm(lang), get_confirm(lang)) + + # Setting the default language via empty data + set_language(client, "en") + assert client.features.language == "enUS" + _check_ping_screen_texts(client, get_confirm("en"), get_confirm("en")) def test_language_stays_after_wipe(client: Client): - with _set_english_return_back(client) as client: - assert client.features.language == "en-US" + assert client.features.language == "enUS" - _check_ping_screen_texts(client, ENGLISH_CONFIRM, ENGLISH_CONFIRM) + _check_ping_screen_texts(client, get_confirm("en"), get_confirm("en")) - # Setting cs language - _set_full_czech(client) - assert client.features.language == "cs" + # Setting cs language + set_language(client, "cs") + assert client.features.language == "csCZ" - _check_ping_screen_texts(client, CZECH_CONFIRM, CZECH_CONFIRM) + _check_ping_screen_texts(client, get_confirm("cs"), get_confirm("cs")) - # Wipe device - device.wipe(client) - assert client.features.language == "cs" + # Wipe device + device.wipe(client) + assert client.features.language == "csCZ" - # Load it again - debuglink.load_device( - client, - mnemonic=" ".join(["all"] * 12), - pin=None, - passphrase_protection=False, - label="test", - ) - assert client.features.language == "cs" + # Load it again + debuglink.load_device( + client, + mnemonic=" ".join(["all"] * 12), + pin=None, + passphrase_protection=False, + label="test", + ) + assert client.features.language == "csCZ" - _check_ping_screen_texts(client, CZECH_CONFIRM, CZECH_CONFIRM) + _check_ping_screen_texts(client, get_confirm("cs"), get_confirm("cs")) def test_translations_renders_on_screen(client: Client): - with open(CS_JSON, "r") as f: - czech_data = json.load(f) + czech_data = get_lang_json("cs") # Setting some values of words__confirm key and checking that in ping screen title - with _set_english_return_back(client) as client: - assert client.features.language == "en-US" + assert client.features.language == "enUS" - # Normal english - _check_ping_screen_texts(client, ENGLISH_CONFIRM, ENGLISH_CONFIRM) + # Normal english + _check_ping_screen_texts(client, get_confirm("en"), get_confirm("en")) - # Normal czech - _set_full_czech(client) - assert client.features.language == "cs" - _check_ping_screen_texts(client, CZECH_CONFIRM, CZECH_CONFIRM) + # Normal czech + set_language(client, "cs") + assert client.features.language == "csCZ" + _check_ping_screen_texts(client, get_confirm("cs"), get_confirm("cs")) - # Modified czech - changed value - czech_data_copy = deepcopy(czech_data) - new_czech_confirm = "ABCD" - czech_data_copy["translations"]["words"]["confirm"] = new_czech_confirm - device.change_language( - client, - language_data=_get_data_from_dict(czech_data_copy, client), - ) - _check_ping_screen_texts(client, new_czech_confirm, CZECH_CONFIRM) + # Modified czech - changed value + czech_data_copy = deepcopy(czech_data) + new_czech_confirm = "ABCD" + czech_data_copy["translations"]["words__confirm"] = new_czech_confirm + device.change_language( + client, + language_data=build_and_sign_blob(czech_data_copy, client.model), + ) + _check_ping_screen_texts(client, new_czech_confirm, get_confirm("cs")) - # Modified czech - key deleted completely, english is shown - czech_data_copy = deepcopy(czech_data) - del czech_data_copy["translations"]["words"]["confirm"] - device.change_language( - client, language_data=_get_data_from_dict(czech_data_copy, client) - ) - _check_ping_screen_texts(client, ENGLISH_CONFIRM, CZECH_CONFIRM) + # Modified czech - key deleted completely, english is shown + czech_data_copy = deepcopy(czech_data) + del czech_data_copy["translations"]["words__confirm"] + device.change_language( + client, + language_data=build_and_sign_blob(czech_data_copy, client.model), + ) + _check_ping_screen_texts(client, get_confirm("en"), get_confirm("cs")) diff --git a/tests/translations.py b/tests/translations.py index 8bf51f865..0feb79013 100644 --- a/tests/translations.py +++ b/tests/translations.py @@ -1,32 +1,84 @@ import json +import typing as t +from hashlib import sha256 from pathlib import Path -from typing import Any, Iterable + +from trezorlib import device, models +from trezorlib._internal import translations +from trezorlib.debuglink import TrezorClientDebugLink as Client + +from . import common HERE = Path(__file__).resolve().parent ROOT = HERE.parent -TRANSLATIONS = ROOT / "core" / "embed" / "rust" / "src" / "ui" / "translations" +TRANSLATIONS = ROOT / "core" / "translations" +FONTS_DIR = TRANSLATIONS / "fonts" +ORDER_FILE = TRANSLATIONS / "order.json" + +LANGUAGES = [file.stem for file in TRANSLATIONS.glob("??.json")] + + +def build_and_sign_blob( + lang_or_def: translations.JsonDef | Path | str, model: models.TrezorModel +) -> bytes: + order = translations.order_from_json(json.loads(ORDER_FILE.read_text())) + if isinstance(lang_or_def, str): + lang_or_def = get_lang_json(lang_or_def) + if isinstance(lang_or_def, Path): + lang_or_def = t.cast(translations.JsonDef, json.loads(lang_or_def.read_text())) + + # generate raw blob + blob = translations.blob_from_defs(lang_or_def, order, model, FONTS_DIR) + + # build 0-item Merkle proof + digest = sha256(b"\x00" + blob.header_bytes).digest() + signature = common.sign_with_privkeys(digest, common.PRIVATE_KEYS_DEV) + blob.proof = translations.Proof( + merkle_proof=[], + sigmask=0b111, + signature=signature, + ) + return blob.build() -LANGUAGES = ["cs", "en", "fr"] +def set_language(client: Client, lang: str): + if lang.startswith("en"): + language_data = b"" + else: + language_data = build_and_sign_blob(lang, client.model) + with client: + device.change_language(client, language_data) -def _get_all_language_data() -> list[dict[str, dict[str, str]]]: + +def get_lang_json(lang: str) -> translations.JsonDef: + assert lang in LANGUAGES + return json.loads((TRANSLATIONS / f"{lang}.json").read_text()) + + +def _get_all_language_data() -> list[dict[str, str]]: return [_get_language_data(language) for language in LANGUAGES] -def _get_language_data(language: str) -> dict[str, dict[str, str]]: - file = TRANSLATIONS / f"{language}.json" - return json.loads(file.read_text())["translations"] +def _get_language_data(lang: str) -> dict[str, str]: + defs = get_lang_json(lang) + # TODO: remove this before merge + translations = { + k: v.replace(" (TODO)", "") for k, v in defs["translations"].items() + } + return translations all_language_data = _get_all_language_data() -def _resolve_path_to_texts(path: str, template: Iterable[Any] = ()) -> list[str]: +def _resolve_path_to_texts( + path: str, template: t.Iterable[t.Any] = (), lower: bool = True +) -> list[str]: texts: list[str] = [] lookups = path.split(".") for language_data in all_language_data: - data: dict[str, Any] | str = language_data + data: dict[str, t.Any] | str = language_data for lookup in lookups: assert isinstance(data, dict), f"{lookup} is not a dict" data = data[lookup] @@ -34,27 +86,39 @@ def _resolve_path_to_texts(path: str, template: Iterable[Any] = ()) -> list[str] if template: data = data.format(*template) texts.append(data) + + if lower: + texts = [t.lower() for t in texts] return texts -def assert_equals(text: str, path: str, template: Iterable[Any] = ()) -> None: +def assert_equals(text: str, path: str, template: t.Iterable[t.Any] = ()) -> None: # TODO: we can directly pass in the current device language texts = _resolve_path_to_texts(path, template) - assert text in texts, f"{text} not found in {texts}" + assert text.lower() in texts, f"{text} not found in {texts}" + + +def assert_equals_multiple( + text: str, paths: list[str], template: t.Iterable[t.Any] = () +) -> None: + texts: list[str] = [] + for path in paths: + texts += _resolve_path_to_texts(path, template) + assert text.lower() in texts, f"{text} not found in {texts}" -def assert_in(text: str, path: str, template: Iterable[Any] = ()) -> None: +def assert_in(text: str, path: str, template: t.Iterable[t.Any] = ()) -> None: texts = _resolve_path_to_texts(path, template) for t in texts: - if t in text: + if t in text.lower(): return assert False, f"{text} not found in {texts}" -def assert_startswith(text: str, path: str, template: Iterable[Any] = ()) -> None: +def assert_startswith(text: str, path: str, template: t.Iterable[t.Any] = ()) -> None: texts = _resolve_path_to_texts(path, template) for t in texts: - if text.startswith(t): + if text.lower().startswith(t): return assert False, f"{text} not found in {texts}" @@ -64,10 +128,13 @@ def assert_template(text: str, template_path: str) -> None: for t in templates: # Checking at least the first part first_part = t.split("{")[0] - if text.startswith(first_part): + if text.lower().startswith(first_part): return assert False, f"{text} not found in {templates}" -def translate(path: str, template: Iterable[Any] = ()) -> list[str]: - return _resolve_path_to_texts(path, template) +def translate( + path: str, template: t.Iterable[t.Any] = (), lower: bool = False +) -> list[str]: + # Do not converting to lowercase, we want the exact value + return _resolve_path_to_texts(path, template, lower=lower) diff --git a/tests/ui_tests/common.py b/tests/ui_tests/common.py index 1482c22ee..3c81dc280 100644 --- a/tests/ui_tests/common.py +++ b/tests/ui_tests/common.py @@ -17,6 +17,7 @@ import pytest from PIL import Image from typing_extensions import Self +from trezorlib import models from trezorlib.debuglink import TrezorClientDebugLink as Client UI_TESTS_DIR = Path(__file__).resolve().parent @@ -36,10 +37,7 @@ FixturesType = t.NewType("FixturesType", "dict[str, dict[str, dict[str, str]]]") FIXTURES: FixturesType = FixturesType({}) -ENGLISH_LANGUAGE_TREZOR = "en-US" ENGLISH_LANGUAGE = "en" -FOREIGN_LANGUAGES = ["cs", "fr"] -SUPPORTED_LANGUAGES = FOREIGN_LANGUAGES + [ENGLISH_LANGUAGE] def get_current_fixtures() -> FixturesType: @@ -236,17 +234,18 @@ class TestCase: @classmethod def build(cls, client: Client, request: pytest.FixtureRequest) -> Self: - model = client.features.model # FIXME - if model == "Safe 3": - model = "R" + if client.model is models.T2B1: + model_name = "R" + else: + model_name = client.model.name name, group = _get_test_name_and_group(request.node.nodeid) - language = client.features.language or "" - if language == ENGLISH_LANGUAGE_TREZOR: + if client.features.language: + language = client.features.language[:2] + else: language = ENGLISH_LANGUAGE - assert language in SUPPORTED_LANGUAGES return cls( - model=f"T{model}", + model=f"T{model_name}", name=name, group=group, language=language, @@ -254,8 +253,9 @@ class TestCase: @staticmethod def get_language_from_fixture_name(fixture_name: str) -> str: + # FIXME lang = fixture_name.split("_")[1] - if lang in FOREIGN_LANGUAGES: + if len(lang) == 2: return lang # English (currently) is implicit there return ENGLISH_LANGUAGE