1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2024-11-18 05:28:40 +00:00
trezor-firmware/tests/click_tests/test_passphrase_tr.py
grdddj 7f1a5ac4c1 WIP - firmware translations
WIP - refactor and extend font generation for non-ascii characters

WIP - add czech characters mapping between UTF8 value and index

WIP - regenerate font files with czech characters

WIP - shorten czech button text, it was causing SHUTDOWN for some reason

WIP - support UTF8 characters in fonts.c

WIP - account for translation in tests

WIP - small fixes

WIP - fix last test

WIP - support UTF8 also in Rust font operations

WIP - add a script to find non-translated english strings in micropython code

WIP - add a validator script for checking missing micropython translations

WIP - translate remaining altcoins and other apps in core (fido, sdcard, TT layouts, ...)

WIP - generate czech glyphs for TT fonts

WIP - modify gen_font.py to account for negative bearing czech characters

WIP - extend translation validation scripts, move them into core/tools

WIP - translate TT layouts in Rust

WIP - fix tests

WIP - fix inverse coloring of nonprintable glyph

WIP - add build and test pipelines for Czech language

WIP - merge both JSON files together

WIP - run new isort

WIP - unify all the translation in Rust, expose to micropython

TEMP - leave en_merged.json file, so it is accessible by translators with old link

WIP - fixes

WIP - add french characters and translation via Google Translator

WIP - skip rustfmt in mako-created files

WIP - revert all the font height changes causing false-positive UI diff

WIP - fixes after rebase

WIP - fix broken translations

WIP - revert some wording changes causing UI diff

WIP - improve validation and translate scripts, translate missing strings

WIP - sort all keys alphabetically

WIP - remove any usage of translation in bootloader

WIP - add newline at the end of JSON file

WIP - fix bitcoin-only strings check

WIP - fix python support check

WIP - add some missing translations

WIP - fix SD card device test

WIP - fix pystyle

WIP - fix rust unittests

WIP - fix click tests

WIP - flag errors in french translations

WIP - add script transferring translations data into a byte blob

WIP - regenerate fr.rs

WIP - store and read language translations from flash

WIP - storing language name in storage

WIP - sending language_data in apply_settings protobuf message

WIP - separate protobuf message for translations, fixes

WIP - set up translations area for TT as well

WIP - get rid of TREZOR_LANG env variable during build

WIP - make the firmware buildable for TT

WIP - add basic device tests

WIP - set language for tests

WIP - counting with language when writing fixtures

WIP - add todos

WIP - fix CI

WIP - unify translations, make titles CAPITAL

WIP - translate missing english

WIP - skip translations messages for T1

WIP - not changing tests names for english

WIP - fix flake8

WIP - no test language setting for T1

WIP - clippy lint about complex data type

WIP - fix some english UI diff for TR

WIP - fix cstyle

WIP - minimize the usage of #[cfg(feature = "micropython")] outside translations module

WIP - minimize TT's UI diff

WIP - fix ruststyle

WIP - fix TR build

WIP - advanced Shamir text change

WIP - storing the language name as the first item in the translation data

WIP - modify and extend tests after storing language name

WIP - modify checklist sentence

WIP - add TEST_LANG into Makefile for all the emu tests

WIP - default arguments

WIP - reimplement default arguments

remove unneeded pub from get_info function

WIP - Rust handling of object attributes lookups from upy - thanks Matejcik!

WIP - generate mock interface for attribute-based translations lookups

WIP - change function calls to object attributes

WIP - symbolic link for unix/translations.c

WIP - fix and improve the reading of translations - thanks Matejcik!

WIP - add support for multiple languages in removing missing tests

WIP - fix multiple-accounts warning in tests

WIP - fix encoding of newlines in translations

WIP - fix czech tutorial text

WIP - fix czech click tests

WIP - do not translate wire error messages

WIP - add language options to click tests as well

WIP - setup czech device tests in CI

WIP - setup czech click tests in CI

WIP - record czech device tests for TR

WIP - record czech click tests for TR

WIP - record czech device tests for TT

WIP - record czech click tests for TT

WIP - pystyle

WIP - cstyle

WIP - fix Rust micropython import dependency

WIP - fix czech recordings

WIP - support french translations in tests

WIP - shorten some french words to fix the tests

WIP - fix micropython cfg compilation

WIP - record french click tests for TR

WIP - record french device tests for TR

WIP - record french device tests for TT

WIP - record french click tests for TT

WIP - fix french translations - shorten them

WIP - translate missing french words

WIP - fix click tests

WIP - add french tests into CI

WIP - pystyle

WIP - allow for czech/french tests in update script

WIP - update czech fixtures

WIP - update french fixtures

WIP - ruststyle

WIP - disallow MPU to run it on hardware

WIP - cstyle

WIP - change translations delimiter from * to \x00

WIP - change translations protobufs

WIP - remove language handling from storage

WIP - add header into JSON files

WIP - count with header in translations blob

WIP - yml style fixes

WIP - fix proto gen

WIP - verify version and data hash

WIP - fix loading test translations

feat(core): allow access to translations area in firmware

[no changelog]

WIP - fixes after rebase

WIP - increase the TT's translations area to 3 sectors

WIP - dynamically read the maximum translations size

WIP - record non-english tests from CI

WIP - loading font data from translations blob

WIP - bump translations version

WIP - include czech and french glyph data

WIP - whitelist another negative-bearing glyph

WIP - remove czech/french glyphs from common font files

WIP - fix language tests

WIP - specific fonts for specific models

WIP - revert the non-ascii font hardcoding

WIP - include missing BIG font into nonprintable logic

WIP - minor Rust code improvements

WIP - include newlines at the end of json files

WIP - move glyph Rust function to librust_fonts.h

WIP - add all fonts into translations file

WIP - move fonts into its own dir

WIP - reflect separate dir for fonts

WIP - not putting translations trezorhal into bootloader

WIP - write and read multiple fonts into translations data

WIP - silence pyright issue/notissue

WIP - delete no more used translations/*.py imports

WIP - fix bootloader builds by introducing translations feature and TRANSLATIONS flag

WIP - fix TT's bootloader Rust build

WIP - fix tests in non-english languages

WIP - not search for UTF-8 when there are no translations data

WIP - add colons to strings where missing

WIP - fix language loading in tests

WIP - fix signmessage input flow to work in all languages

WIP - create offset table for translation strings

WIP - code improvements

WIP - record foreign language fixtures + sync with main in english

WIP - do alignment check before reading u16 data

WIP - allocate blob in RAM for translations data

WIP - add TODO for blob generation

WIP - record non-english device tests

WIP - use bytes.align_to instead of messing with pointers

WIP - fixtures

WIP - remove unused import

WIP - add order.py

WIP - add order.json

WIP - take order.json into account in creating general.rs

WIP - take order.json into account in generating the blob

WIP - style

WIP - sort the language files

WIP - remove unused file

WIP - code improvements

WIP - add TODO for homescreen notification

WIP - translate plural forms

WIP - translate time intervals

WIP - sign translations with dev keys, validate signatures, improve robustness

WIP - improve tests for translations

WIP - add `trezorctl utils sign-translations` for production signing of the blob

WIP - pyright fix

WIP - changing TR progress loader offset - it was colliding with title

WIP - show indeterminate loader when loading translations data

WIP - record new and updated language tests

WIP - show the change language title/prompt in the target language

WIP - sort keys

WIP - add crowdin-cli into shell.nix

WIP - add crowdin sync script
2024-01-02 14:55:16 +01:00

277 lines
9.0 KiB
Python

# This file is part of the Trezor project.
#
# Copyright (C) 2012-2023 SatoshiLabs and contributors
#
# This library is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License version 3
# as published by the Free Software Foundation.
#
# This library 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 Lesser General Public License for more details.
#
# You should have received a copy of the License along with this library.
# If not, see <https://www.gnu.org/licenses/lgpl-3.0.html>.
from contextlib import contextmanager
from typing import TYPE_CHECKING, Generator, Optional
import pytest
from trezorlib import exceptions
from .. import translations as TR
from ..common import get_test_address
from .common import (
CommonPass,
PassphraseCategory,
get_char_category,
navigate_to_action_and_press,
)
if TYPE_CHECKING:
from trezorlib.debuglink import DebugLink
from ..device_handler import BackgroundDeviceHandler
pytestmark = [pytest.mark.skip_t1, pytest.mark.skip_t2]
# Testing the maximum length is really 50
# TODO: show some UI message when length reaches 50?
AAA_50 = 50 * "a"
AAA_50_ADDRESS = "miPeCUxf1Ufh5DtV3AuBopNM8YEDvnQZMh"
assert len(AAA_50) == 50
AAA_49 = AAA_50[:-1]
AAA_49_ADDRESS = "n2MPUjAB86MuVmyYe8HCgdznJS1FXk3qvg"
assert len(AAA_49) == 49
assert AAA_49_ADDRESS != AAA_50_ADDRESS
AAA_51 = AAA_50 + "a"
AAA_51_ADDRESS = "miPeCUxf1Ufh5DtV3AuBopNM8YEDvnQZMh"
assert len(AAA_51) == 51
assert AAA_51_ADDRESS == AAA_50_ADDRESS
def _get_possible_btns(path: str) -> str:
return "|".join(TR.translate(path))
def _get_cancel_or_delete() -> str:
paths = ["inputs.cancel", "inputs.delete"]
return "|".join(_get_possible_btns(path) for path in paths)
BACK = _get_possible_btns("inputs.back")
SHOW = _get_possible_btns("inputs.show")
ENTER = _get_possible_btns("inputs.enter")
SPACE = _get_possible_btns("inputs.space")
CANCEL_OR_DELETE = _get_cancel_or_delete()
# fmt: off
MENU_ACTIONS = [SHOW, CANCEL_OR_DELETE, ENTER, "abc", "ABC", "123", "#$!", SPACE]
DIGITS_ACTIONS = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", BACK]
LOWERCASE_ACTIONS = [
"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s",
"t", "u", "v", "w", "x", "y", "z", BACK
]
UPPERCASE_ACTIONS = [
"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S",
"T", "U", "V", "W", "X", "Y", "Z", BACK
]
SPECIAL_ACTIONS = [
"_", "<", ">", ".", ":", "@", "/", "|", "\\", "!", "(", ")", "+", "%", "&", "-", "[", "]", "?",
"{", "}", ",", "'", "`", ";", "\"", "~", "$", "^", "=", "*", "#", BACK
]
# fmt: on
CATEGORY_ACTIONS = {
PassphraseCategory.MENU: MENU_ACTIONS,
PassphraseCategory.DIGITS: DIGITS_ACTIONS,
PassphraseCategory.LOWERCASE: LOWERCASE_ACTIONS,
PassphraseCategory.UPPERCASE: UPPERCASE_ACTIONS,
PassphraseCategory.SPECIAL: SPECIAL_ACTIONS,
}
@contextmanager
def prepare_passphrase_dialogue(
device_handler: "BackgroundDeviceHandler", address: Optional[str] = None
) -> Generator["DebugLink", None, None]:
debug = device_handler.debuglink()
device_handler.run(get_test_address) # type: ignore
layout = debug.wait_layout()
assert "PassphraseKeyboard" in layout.all_components()
assert layout.passphrase() == ""
assert _current_category(debug) == PassphraseCategory.MENU
yield debug
result = device_handler.result()
if address is not None:
assert result == address
def _current_category(debug: "DebugLink") -> PassphraseCategory:
"""What is the current category we are in"""
layout = debug.read_layout()
category = layout.find_unique_value_by_key("current_category", "")
return PassphraseCategory(category)
def _current_actions(debug: "DebugLink") -> list[str]:
"""What are the actions in the current category"""
current = _current_category(debug)
return CATEGORY_ACTIONS[current]
def go_to_category(
debug: "DebugLink", category: PassphraseCategory, use_carousel: bool = True
) -> None:
"""Go to a specific category"""
# Already there
if _current_category(debug) == category:
return
# Need to be in MENU anytime to change category
if _current_category(debug) != PassphraseCategory.MENU:
navigate_to_action_and_press(
debug, BACK, _current_actions(debug), is_carousel=use_carousel
)
assert _current_category(debug) == PassphraseCategory.MENU
# Go to the right one, unless we want MENU
if category != PassphraseCategory.MENU:
navigate_to_action_and_press(
debug, category.value, _current_actions(debug), is_carousel=use_carousel
)
assert _current_category(debug) == category
def press_char(debug: "DebugLink", char: str) -> None:
"""Press a character"""
# Space is a special case
if char == " ":
go_to_category(debug, PassphraseCategory.MENU)
navigate_to_action_and_press(debug, SPACE, _current_actions(debug))
else:
char_category = get_char_category(char)
go_to_category(debug, char_category)
navigate_to_action_and_press(debug, char, _current_actions(debug))
def input_passphrase(debug: "DebugLink", passphrase: str) -> None:
"""Input a passphrase with validation it got added"""
before = debug.read_layout().passphrase()
for char in passphrase:
press_char(debug, char)
after = debug.read_layout().passphrase()
assert after == before + passphrase
def show_passphrase(debug: "DebugLink") -> None:
"""Show a passphrase"""
go_to_category(debug, PassphraseCategory.MENU)
navigate_to_action_and_press(debug, SHOW, _current_actions(debug))
def enter_passphrase(debug: "DebugLink") -> None:
"""Enter a passphrase"""
go_to_category(debug, PassphraseCategory.MENU)
navigate_to_action_and_press(debug, ENTER, _current_actions(debug))
def delete_char(debug: "DebugLink") -> None:
"""Deletes the last char"""
go_to_category(debug, PassphraseCategory.MENU)
navigate_to_action_and_press(debug, CANCEL_OR_DELETE, _current_actions(debug))
def cancel(debug: "DebugLink") -> None:
"""Cancels the whole dialogue - clicking the same button as in DELETE"""
delete_char(debug)
VECTORS = ( # passphrase, address
(CommonPass.SHORT, CommonPass.SHORT_ADDRESS),
(CommonPass.WITH_SPACE, CommonPass.WITH_SPACE_ADDRESS),
(CommonPass.RANDOM_25, CommonPass.RANDOM_25_ADDRESS),
(AAA_49, AAA_49_ADDRESS),
(AAA_50, AAA_50_ADDRESS),
)
@pytest.mark.parametrize("passphrase, address", VECTORS)
@pytest.mark.setup_client(passphrase=True)
def test_passphrase_input(
device_handler: "BackgroundDeviceHandler", passphrase: str, address: str
):
with prepare_passphrase_dialogue(device_handler, address) as debug:
input_passphrase(debug, passphrase)
show_passphrase(debug)
enter_passphrase(debug)
@pytest.mark.setup_client(passphrase=True)
def test_passphrase_input_over_50_chars(device_handler: "BackgroundDeviceHandler"):
with prepare_passphrase_dialogue(device_handler, AAA_51_ADDRESS) as debug: # type: ignore
# First 50 chars
input_passphrase(debug, AAA_51[:-1])
layout = debug.read_layout()
assert AAA_51[:-1] in layout.passphrase()
show_passphrase(debug)
# Over-limit character
press_char(debug, AAA_51[-1])
# No change
layout = debug.read_layout()
assert AAA_51[:-1] in layout.passphrase()
assert AAA_51 not in layout.passphrase()
show_passphrase(debug)
enter_passphrase(debug)
@pytest.mark.setup_client(passphrase=True)
def test_passphrase_delete(device_handler: "BackgroundDeviceHandler"):
with prepare_passphrase_dialogue(device_handler, CommonPass.SHORT_ADDRESS) as debug:
input_passphrase(debug, CommonPass.SHORT[:8])
show_passphrase(debug)
for _ in range(4):
delete_char(debug)
show_passphrase(debug)
input_passphrase(debug, CommonPass.SHORT[8 - 4 :])
show_passphrase(debug)
enter_passphrase(debug)
@pytest.mark.setup_client(passphrase=True)
def test_cancel(device_handler: "BackgroundDeviceHandler"):
with pytest.raises(exceptions.Cancelled):
with prepare_passphrase_dialogue(device_handler) as debug:
input_passphrase(debug, "abc")
show_passphrase(debug)
for _ in range(3):
delete_char(debug)
show_passphrase(debug)
cancel(debug)
@pytest.mark.setup_client(passphrase=True)
def test_passphrase_loop_all_characters(device_handler: "BackgroundDeviceHandler"):
with prepare_passphrase_dialogue(device_handler, CommonPass.EMPTY_ADDRESS) as debug:
for category in PassphraseCategory:
go_to_category(debug, category)
# use_carousel=False because we want to reach BACK at the end of the list
go_to_category(debug, PassphraseCategory.MENU, use_carousel=False)
enter_passphrase(debug)