1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2025-04-22 18:19:03 +00:00

feat(tests): eckhart tests

add Eckhart screen buttons
rename passphrase click test file + fixtures
eckhart click tests
eckhart persistance tests
eckhart device tests
skip tests for unimplemented features
This commit is contained in:
Lukas Bielesch 2025-03-09 18:55:16 +01:00 committed by obrusvit
parent ca421dbabc
commit 69004f450f
78 changed files with 4606 additions and 501 deletions

View File

@ -120,6 +120,7 @@ Arguments can be a list of internal model names, or one of the following shortcu
* `safe` - Trezor Safe family
* `safe3` - Trezor Safe 3 (covers T2B1 and T2T1)
* `delizia` - covers the `delizia` UI (currently T3T1 only)
* `eckhart` - covers the `eckhart` UI (currently T3W1 only)
You can specify a list as positional arguments, and exclude from it via `skip` keyword argument.

View File

@ -403,6 +403,20 @@ class LayoutContent(UnstructuredJSONReader):
return ""
return footer.get("description", "") + " " + footer.get("instruction", "")
def action_bar(self) -> str:
action_bar = self.find_unique_object_with_key_and_value(
"component", "ActionBar"
)
if not action_bar:
return ""
right_button = action_bar.get("right_button", "")
left_button = action_bar.get("left_button", "")
if isinstance(left_button, dict):
left_button = left_button.get("text", "")
if isinstance(right_button, dict):
right_button = right_button.get("text", "")
return left_button + " " + right_button
def multipage_content(layouts: list[LayoutContent]) -> str:
"""Get overall content from multiple-page layout."""
@ -829,7 +843,10 @@ class DebugUI:
# Paginating (going as further as possible) and pressing Yes
if br.pages is not None:
for _ in range(br.pages - 1):
self.debuglink.swipe_up()
if self.debuglink.model is models.T3W1:
self.debuglink.click(self.debuglink.screen_buttons.ok())
else:
self.debuglink.swipe_up()
if self.debuglink.model is models.T3T1:
layout = self.debuglink.read_layout()
@ -839,6 +856,12 @@ class DebugUI:
self.debuglink.swipe_up()
else:
self.debuglink.press_yes()
elif self.debuglink.model is models.T3W1:
layout = self.debuglink.read_layout()
if "TextComponent" in layout.all_components():
self.debuglink.click(self.debuglink.screen_buttons.ok())
else:
self.debuglink.press_yes()
else:
self.debuglink.press_yes()
@ -1413,14 +1436,24 @@ def optiga_set_sec_max(client: "TrezorClient") -> None:
class ScreenButtons:
def __init__(self, layout_type: LayoutType):
assert layout_type in (LayoutType.Bolt, LayoutType.Delizia)
assert layout_type in (LayoutType.Bolt, LayoutType.Delizia, LayoutType.Eckhart)
self.layout_type = layout_type
def _width(self) -> int:
return 240
if self.layout_type in (LayoutType.Bolt, LayoutType.Delizia):
return 240
elif self.layout_type is LayoutType.Eckhart:
return 380
else:
raise ValueError("Wrong layout type")
def _height(self) -> int:
return 240
if self.layout_type in (LayoutType.Bolt, LayoutType.Delizia):
return 240
elif self.layout_type is LayoutType.Eckhart:
return 520
else:
raise ValueError("Wrong layout type")
def _grid(self, dim: int, grid_cells: int, cell: int) -> int:
assert cell < grid_cells
@ -1430,13 +1463,24 @@ class ScreenButtons:
# 3 columns, 4 rows, 1st row is input area
def _grid35(self, x: int, y: int) -> Coords:
assert x < 3, y < 5
return self._grid(self._width(), 3, x), self._grid(self._height(), 5, y)
def _grid55(self, x: int, y: int) -> Coords:
assert x < 5, y < 5
return self._grid(self._width(), 5, x), self._grid(self._height(), 5, y)
# TODO: do not expose this
# 3 columns, 3 rows, 1st row is input area
def grid34(self, x: int, y: int) -> Coords:
assert x < 3, y < 4
return self._grid(self._width(), 3, x), self._grid(self._height(), 4, y)
# 2 columns, 3 rows, first two are header and description
def _grid25(self, x: int, y: int) -> Coords:
assert x < 2, y < 5
return self._grid(self._width(), 2, x), self._grid(self._height(), 5, y)
# Horizontal coordinates
def _left(self) -> int:
return self._grid(self._width(), 3, 0)
@ -1470,10 +1514,7 @@ class ScreenButtons:
# Menu/close menu button
def menu(self) -> Coords:
if self.layout_type in (LayoutType.Bolt, LayoutType.Delizia):
return (215, 25)
else:
raise ValueError("Wrong layout type")
return self._grid55(4, 0)
# Center of the screen
def tap_to_confirm(self) -> Coords:
@ -1482,12 +1523,20 @@ class ScreenButtons:
# Yes/No decision component
def ui_yes(self) -> Coords:
assert self.layout_type is LayoutType.Delizia
return self.grid34(2, 2)
if self.layout_type is LayoutType.Delizia:
return self.grid34(2, 2)
elif self.layout_type is LayoutType.Eckhart:
return self.ok()
else:
raise ValueError("Wrong layout type")
def ui_no(self) -> Coords:
assert self.layout_type is LayoutType.Delizia
return self.grid34(0, 2)
if self.layout_type is LayoutType.Delizia:
return self.grid34(0, 2)
elif self.layout_type is LayoutType.Eckhart:
return self.cancel()
else:
raise ValueError("Wrong layout type")
# +/- buttons in number input component
def number_input_minus(self) -> Coords:
@ -1495,6 +1544,8 @@ class ScreenButtons:
return (self._left(), self._grid(self._height(), 5, 1))
elif self.layout_type is LayoutType.Delizia:
return (self._left(), self._grid(self._height(), 5, 3))
elif self.layout_type is LayoutType.Eckhart:
return self.grid34(0, 2)
else:
raise ValueError("Wrong layout type")
@ -1503,6 +1554,8 @@ class ScreenButtons:
return (self._right(), self._grid(self._height(), 5, 1))
elif self.layout_type is LayoutType.Delizia:
return (self._right(), self._grid(self._height(), 5, 3))
elif self.layout_type is LayoutType.Eckhart:
return self.grid34(2, 2)
else:
raise ValueError("Wrong layout type")
@ -1524,6 +1577,14 @@ class ScreenButtons:
24: self.grid34(2, 2),
33: self.grid34(2, 3),
}
elif self.layout_type is LayoutType.Eckhart:
coords_map = {
12: self._grid35(0, 2),
18: self._grid35(2, 2),
20: self._grid35(0, 3),
24: self._grid35(2, 3),
33: self._grid35(2, 4),
}
else:
raise ValueError("Wrong layout type")
@ -1532,10 +1593,10 @@ class ScreenButtons:
def word_count_all_cancel(self) -> Coords:
if self.layout_type is LayoutType.Bolt:
return self.grid34(0, 3)
elif self.layout_type is LayoutType.Delizia:
return self.grid34(0, 3)
elif self.layout_type is LayoutType.Eckhart:
return self._grid35(0, 4)
else:
raise ValueError("Wrong layout type")
@ -1551,6 +1612,11 @@ class ScreenButtons:
20: self.grid34(0, 1),
33: self.grid34(2, 1),
}
elif self.layout_type is LayoutType.Eckhart:
coords_map = {
20: self._grid35(1, 2),
33: self._grid35(1, 3),
}
else:
raise ValueError("Wrong layout type")
@ -1559,16 +1625,22 @@ class ScreenButtons:
def word_count_repeated_cancel(self) -> Coords:
if self.layout_type is LayoutType.Bolt:
return self.grid34(0, 2)
elif self.layout_type is LayoutType.Delizia:
return self.grid34(0, 3)
elif self.layout_type is LayoutType.Eckhart:
return self._grid35(1, 4)
else:
raise ValueError("Wrong layout type")
# select word component buttons
def word_check_words(self) -> "list[Coords]":
if self.layout_type in (LayoutType.Bolt, LayoutType.Delizia):
return [
(self._mid(), self._grid(self._height(), 4, 1)),
(self._mid(), self._grid(self._height(), 4, 2)),
(self._mid(), self._grid(self._height(), 4, 3)),
]
elif self.layout_type is LayoutType.Eckhart:
return [
(self._mid(), self._grid(self._height(), 5, 2)),
(self._mid(), self._grid(self._height(), 5, 3)),
@ -1579,13 +1651,20 @@ class ScreenButtons:
# vertical menu buttons
def vertical_menu_items(self) -> "list[Coords]":
assert self.layout_type is LayoutType.Delizia
return [
(self._mid(), self._grid(self._height(), 4, 1)),
(self._mid(), self._grid(self._height(), 4, 2)),
(self._mid(), self._grid(self._height(), 4, 3)),
]
if self.layout_type is LayoutType.Delizia:
return [
(self._mid(), self._grid(self._height(), 4, 1)),
(self._mid(), self._grid(self._height(), 4, 2)),
(self._mid(), self._grid(self._height(), 4, 3)),
]
elif self.layout_type is LayoutType.Eckhart:
return [
(self._mid(), self._grid(self._height(), 5, 1)),
(self._mid(), self._grid(self._height(), 5, 2)),
(self._mid(), self._grid(self._height(), 5, 3)),
]
else:
raise ValueError("Wrong layout type")
# Pin/passphrase keyboards
def pin_passphrase_index(self, idx: int) -> Coords:
@ -1595,7 +1674,6 @@ class ScreenButtons:
return self.pin_passphrase_grid(idx % 3, idx // 3)
def pin_passphrase_grid(self, x: int, y: int) -> Coords:
assert x < 3, y < 4
y += 1 # first line is empty
return self._grid35(x, y)
@ -1607,10 +1685,10 @@ class ScreenButtons:
return self.pin_passphrase_grid(0, 3)
def passphrase_confirm(self) -> Coords:
if self.layout_type is LayoutType.Bolt:
if self.layout_type in (LayoutType.Bolt, LayoutType.Eckhart):
return self.pin_passphrase_grid(2, 3)
elif self.layout_type is LayoutType.Delizia:
return (215, 25)
return self._grid55(4, 0)
else:
raise ValueError("Wrong layout type")
@ -1619,19 +1697,34 @@ class ScreenButtons:
# Mnemonic keyboard
def mnemonic_from_index(self, idx: int) -> Coords:
assert idx < 9
return self.mnemonic_grid(idx)
def mnemonic_grid(self, idx: int) -> Coords:
assert idx < 9
grid_x = idx % 3
grid_y = idx // 3 + 1 # first line is empty
return self.grid34(grid_x, grid_y)
if self.layout_type in (LayoutType.Bolt, LayoutType.Delizia):
return self.grid34(grid_x, grid_y)
elif self.layout_type is LayoutType.Eckhart:
return self._grid35(grid_x, grid_y)
else:
raise ValueError("Wrong layout type")
def mnemonic_erase(self) -> Coords:
return (self._left(), self._top())
if self.layout_type in (LayoutType.Bolt, LayoutType.Delizia):
return (self._left(), self._top())
elif self.layout_type is LayoutType.Eckhart:
return self._grid35(0, 4)
else:
raise ValueError("Wrong layout type")
def mnemonic_confirm(self) -> Coords:
return (self._mid(), self._top())
if self.layout_type in (LayoutType.Bolt, LayoutType.Delizia):
return (self._mid(), self._top())
elif self.layout_type is LayoutType.Eckhart:
return self._grid35(2, 4)
else:
raise ValueError("Wrong layout type")
BUTTON_LETTERS_BIP39 = ("abc", "def", "ghi", "jkl", "mno", "pqr", "stu", "vwx", "yz")
@ -1639,9 +1732,9 @@ BUTTON_LETTERS_SLIP39 = ("ab", "cd", "ef", "ghij", "klm", "nopq", "rs", "tuv", "
# fmt: off
PASSPHRASE_LOWERCASE_BOLT = (" ", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz", "*#")
PASSPHRASE_LOWERCASE_DELIZIA = ("abc", "def", "ghi", "jkl", "mno", "pq", "rst", "uvw", "xyz", " *#")
PASSPHRASE_LOWERCASE_DE = ("abc", "def", "ghi", "jkl", "mno", "pq", "rst", "uvw", "xyz", " *#")
PASSPHRASE_UPPERCASE_BOLT = (" ", "ABC", "DEF", "GHI", "JKL", "MNO", "PQRS", "TUV", "WXYZ", "*#")
PASSPHRASE_UPPERCASE_DELIZIA = ("ABC", "DEF", "GHI", "JKL", "MNO", "PQ", "RST", "UVW", "XYZ", " *#")
PASSPHRASE_UPPERCASE_DE = ("ABC", "DEF", "GHI", "JKL", "MNO", "PQ", "RST", "UVW", "XYZ", " *#")
PASSPHRASE_DIGITS = ("1", "2", "3", "4", "5", "6", "7", "8", "9", "0")
PASSPHRASE_SPECIAL = ("_<>", ".:@", "/|\\", "!()", "+%&", "-[]", "?{}", ",'`", ";\"~", "$^=")
# fmt: on
@ -1655,15 +1748,15 @@ class ButtonActions:
if char in " *#" or char.islower():
if self.buttons.layout_type is LayoutType.Bolt:
return PASSPHRASE_LOWERCASE_BOLT
elif self.buttons.layout_type is LayoutType.Delizia:
return PASSPHRASE_LOWERCASE_DELIZIA
elif self.buttons.layout_type in (LayoutType.Delizia, LayoutType.Eckhart):
return PASSPHRASE_LOWERCASE_DE
else:
raise ValueError("Wrong layout type")
elif char.isupper():
if self.buttons.layout_type is LayoutType.Bolt:
return PASSPHRASE_UPPERCASE_BOLT
elif self.buttons.layout_type is LayoutType.Delizia:
return PASSPHRASE_UPPERCASE_DELIZIA
elif self.buttons.layout_type in (LayoutType.Delizia, LayoutType.Eckhart):
return PASSPHRASE_UPPERCASE_DE
else:
raise ValueError("Wrong layout type")
elif char.isdigit():

View File

@ -91,7 +91,7 @@ T3B1 = TrezorModel(
)
T3W1 = TrezorModel(
name="T3W1",
name="Safe 7",
internal_name="T3W1",
minimum_version=(2, 1, 0),
vendors=VENDORS,

View File

@ -47,7 +47,7 @@ def get_char_category(char: str) -> PassphraseCategory:
def go_next(debug: "DebugLink") -> LayoutContent:
if debug.layout_type is LayoutType.Bolt:
if debug.layout_type in (LayoutType.Bolt, LayoutType.Eckhart):
debug.click(debug.screen_buttons.ok())
elif debug.layout_type is LayoutType.Caesar:
debug.press_right()
@ -59,7 +59,7 @@ def go_next(debug: "DebugLink") -> LayoutContent:
def go_back(debug: "DebugLink", r_middle: bool = False) -> LayoutContent:
if debug.layout_type in (LayoutType.Bolt, LayoutType.Delizia):
if debug.layout_type in (LayoutType.Bolt, LayoutType.Delizia, LayoutType.Eckhart):
debug.click(debug.screen_buttons.cancel())
elif debug.layout_type is LayoutType.Caesar:
if r_middle:
@ -114,7 +114,7 @@ def _carousel_steps(current_index: int, wanted_index: int, length: int) -> int:
def unlock_gesture(debug: "DebugLink") -> LayoutContent:
if debug.layout_type is LayoutType.Bolt:
if debug.layout_type in (LayoutType.Bolt, LayoutType.Eckhart):
debug.click(debug.screen_buttons.ok())
elif debug.layout_type is LayoutType.Caesar:
debug.press_right()

View File

@ -15,11 +15,15 @@ DELETE_BTN_TEXTS = ("inputs__delete", "inputs__previous")
def enter_word(
debug: "DebugLink", word: str, is_slip39: bool = False
) -> "LayoutContent":
if debug.layout_type in (LayoutType.Bolt, LayoutType.Delizia):
if debug.layout_type in (LayoutType.Bolt, LayoutType.Delizia, LayoutType.Eckhart):
typed_word = word[:4]
for coords in debug.button_actions.type_word(typed_word, is_slip39=is_slip39):
debug.click(coords)
if debug.layout_type is LayoutType.Delizia and not is_slip39 and len(word) > 4:
if (
debug.layout_type in (LayoutType.Delizia, LayoutType.Eckhart)
and not is_slip39
and len(word) > 4
):
# T3T1 (delizia) BIP39 keyboard allows to "confirm" only if the word is fully written, you need to click the word to auto-complete
debug.click(debug.screen_buttons.mnemonic_confirm())
debug.click(debug.screen_buttons.mnemonic_confirm())
@ -53,7 +57,7 @@ def enter_word(
def confirm_recovery(debug: "DebugLink", title: str = "recovery__title") -> None:
layout = debug.read_layout()
assert TR.translate(title) == layout.title()
if debug.layout_type is LayoutType.Bolt:
if debug.layout_type in (LayoutType.Bolt, LayoutType.Eckhart):
debug.click(debug.screen_buttons.ok())
elif debug.layout_type is LayoutType.Delizia:
debug.swipe_up()
@ -80,7 +84,7 @@ def cancel_select_number_of_words(
assert layout.title() == TR.word_count__title
# navigate to the number and confirm it
debug.press_left()
elif debug.layout_type is LayoutType.Delizia:
elif debug.layout_type in (LayoutType.Delizia, LayoutType.Eckhart):
# click the button from ValuePad
if unlock_repeated_backup:
coords = debug.screen_buttons.word_count_repeated_cancel()
@ -99,7 +103,12 @@ def select_number_of_words(
layout = debug.read_layout()
assert TR.recovery__num_of_words in layout.text_content()
def select_bolt() -> "LayoutContent":
def select_bde() -> "LayoutContent":
assert debug.layout_type in (
LayoutType.Bolt,
LayoutType.Delizia,
LayoutType.Eckhart,
)
# click the button from ValuePad
if unlock_repeated_backup:
coords = debug.screen_buttons.word_count_repeated_word(num_of_words)
@ -110,6 +119,7 @@ def select_number_of_words(
return debug.read_layout()
def select_caesar() -> "LayoutContent":
assert debug.layout_type is LayoutType.Caesar
# navigate to the number and confirm it
word_options = (20, 33) if unlock_repeated_backup else (12, 18, 20, 24, 33)
index = word_options.index(num_of_words)
@ -118,24 +128,13 @@ def select_number_of_words(
debug.press_middle()
return debug.read_layout()
def select_delizia() -> "LayoutContent":
# click the button from ValuePad
if unlock_repeated_backup:
coords = debug.screen_buttons.word_count_repeated_word(num_of_words)
else:
coords = debug.screen_buttons.word_count_all_word(num_of_words)
debug.click(coords)
return debug.read_layout()
if debug.layout_type is LayoutType.Bolt:
layout = select_bolt()
if debug.layout_type in (LayoutType.Bolt, LayoutType.Delizia, LayoutType.Eckhart):
layout = select_bde()
elif debug.layout_type is LayoutType.Caesar:
debug.press_right()
layout = debug.read_layout()
assert layout.title() == TR.word_count__title
layout = select_caesar()
elif debug.layout_type is LayoutType.Delizia:
layout = select_delizia()
else:
raise ValueError("Unknown model")
@ -168,7 +167,11 @@ def enter_share(
is_first: bool = True,
before_title: str = "recovery__title_recover",
) -> "LayoutContent":
if debug.layout_type is LayoutType.Caesar:
if debug.layout_type is LayoutType.Bolt:
assert TR.translate(before_title) in debug.read_layout().title()
debug.click(debug.screen_buttons.ok())
layout = debug.read_layout()
elif debug.layout_type is LayoutType.Caesar:
assert TR.translate(before_title) in debug.read_layout().title()
layout = debug.read_layout()
for _ in range(layout.page_count()):
@ -177,10 +180,11 @@ def enter_share(
elif debug.layout_type is LayoutType.Delizia:
debug.swipe_up()
layout = debug.read_layout()
else:
assert TR.translate(before_title) in debug.read_layout().title()
elif debug.layout_type is LayoutType.Eckhart:
debug.click(debug.screen_buttons.ok())
layout = debug.read_layout()
else:
raise ValueError("Unknown model")
assert "MnemonicKeyboard" in layout.all_components()
@ -210,12 +214,18 @@ def enter_shares(
)
if index < len(shares) - 1:
# FIXME: when ui-t3t1 done for shamir, we want to check the template below
assert TR.translate(enter_share_before_title) in debug.read_layout().title()
# TR.assert_in(
# debug.read_layout().text_content(),
# "recovery__x_of_y_entered_template",
# template=(index + 1, len(shares)),
# )
if debug.layout_type is LayoutType.Eckhart:
assert (
TR.translate("recovery__x_of_y_entered_template").format(
index + 1, len(shares)
)
in debug.read_layout().text_content()
)
else:
assert (
TR.translate(enter_share_before_title)
in debug.read_layout().title()
)
assert TR.translate(after_layout_text) in debug.read_layout().text_content()
@ -231,7 +241,6 @@ def enter_seed(
for word in seed_words:
enter_word(debug, word, is_slip39=is_slip39)
assert TR.translate(after_layout_text) in debug.read_layout().text_content()
@ -272,8 +281,8 @@ def enter_seed_previous_correct(
layout = debug.read_layout()
debug.press_middle()
layout = debug.read_layout()
elif debug.layout_type is LayoutType.Delizia:
debug.click(debug.screen_buttons.mnemonic_erase()) # Top-left
elif debug.layout_type in (LayoutType.Delizia, LayoutType.Eckhart):
debug.click(debug.screen_buttons.mnemonic_erase())
for _ in range(len(bad_word)):
debug.click(debug.screen_buttons.mnemonic_erase())
continue
@ -299,7 +308,7 @@ def prepare_enter_seed(
or TR.recovery__enter_each_word in debug.read_layout().text_content()
or TR.translate(layout_text) in debug.read_layout().text_content()
)
if debug.layout_type is LayoutType.Bolt:
if debug.layout_type in (LayoutType.Bolt, LayoutType.Eckhart):
debug.click(debug.screen_buttons.ok())
elif debug.layout_type is LayoutType.Delizia:
debug.swipe_up()
@ -310,6 +319,8 @@ def prepare_enter_seed(
debug.press_right()
layout = debug.read_layout()
assert "MnemonicKeyboard" in layout.all_components()
else:
raise ValueError("Unknown model")
def finalize(debug: "DebugLink") -> None:
@ -336,7 +347,7 @@ def cancel_recovery(debug: "DebugLink", recovery_type: str = "dry_run") -> None:
assert cancel_title in layout.title()
for _ in range(layout.page_count()):
debug.press_right()
elif debug.layout_type is LayoutType.Delizia:
elif debug.layout_type in (LayoutType.Delizia, LayoutType.Eckhart):
# go to menu
debug.click(debug.screen_buttons.menu())
layout = debug.read_layout()

View File

@ -13,7 +13,7 @@ if TYPE_CHECKING:
def confirm_new_wallet(debug: "DebugLink") -> None:
assert debug.read_layout().title() == TR.reset__title_create_wallet
if debug.layout_type is LayoutType.Bolt:
if debug.layout_type in (LayoutType.Bolt, LayoutType.Eckhart):
debug.click(debug.screen_buttons.ok())
elif debug.layout_type is LayoutType.Delizia:
debug.swipe_up()
@ -27,13 +27,13 @@ def confirm_new_wallet(debug: "DebugLink") -> None:
)
if debug.layout_type is LayoutType.Delizia:
debug.swipe_up()
elif debug.layout_type is LayoutType.Eckhart:
debug.click(debug.screen_buttons.ok())
def confirm_read(debug: "DebugLink", middle_r: bool = False) -> None:
if debug.layout_type is LayoutType.Bolt:
if debug.layout_type in (LayoutType.Bolt, LayoutType.Eckhart):
debug.click(debug.screen_buttons.ok())
elif debug.layout_type is LayoutType.Delizia:
debug.swipe_up()
elif debug.layout_type is LayoutType.Caesar:
page_count = debug.read_layout().page_count()
if page_count > 1:
@ -43,6 +43,10 @@ def confirm_read(debug: "DebugLink", middle_r: bool = False) -> None:
debug.press_middle()
else:
debug.press_right()
elif debug.layout_type is LayoutType.Delizia:
debug.swipe_up()
else:
raise RuntimeError("Unknown model")
def cancel_backup(
@ -51,20 +55,29 @@ def cancel_backup(
if debug.layout_type is LayoutType.Bolt:
debug.click(debug.screen_buttons.cancel())
debug.click(debug.screen_buttons.cancel())
elif debug.layout_type is LayoutType.Caesar:
debug.press_left()
debug.press_left()
elif debug.layout_type is LayoutType.Delizia:
debug.click(debug.screen_buttons.menu())
debug.click(debug.screen_buttons.vertical_menu_items()[0])
if confirm:
debug.swipe_up()
debug.click(debug.screen_buttons.tap_to_confirm())
elif debug.layout_type is LayoutType.Caesar:
debug.press_left()
debug.press_left()
elif debug.layout_type is LayoutType.Eckhart:
debug.click(debug.screen_buttons.menu())
debug.click(debug.screen_buttons.vertical_menu_items()[0])
debug.click(debug.screen_buttons.ok())
else:
raise RuntimeError("Unknown model")
def set_selection(debug: "DebugLink", diff: int) -> None:
if debug.layout_type in (LayoutType.Bolt, LayoutType.Delizia):
assert "NumberInputDialog" in debug.read_layout().all_components()
if debug.layout_type in (LayoutType.Bolt, LayoutType.Delizia, LayoutType.Eckhart):
if debug.layout_type is LayoutType.Eckhart:
assert "NumberInputScreen" in debug.read_layout().all_components()
else:
assert "NumberInputDialog" in debug.read_layout().all_components()
button = (
debug.screen_buttons.number_input_minus()
@ -75,7 +88,7 @@ def set_selection(debug: "DebugLink", diff: int) -> None:
for _ in range(diff):
debug.click(button)
if debug.layout_type is LayoutType.Bolt:
if debug.layout_type in (LayoutType.Bolt, LayoutType.Eckhart):
debug.click(debug.screen_buttons.ok())
else:
debug.swipe_up()
@ -96,6 +109,8 @@ def set_selection(debug: "DebugLink", diff: int) -> None:
for _ in range(diff):
debug.press_right()
debug.press_middle()
else:
raise RuntimeError("Unknown model")
def read_words(debug: "DebugLink", do_htc: bool = True) -> list[str]:
@ -105,23 +120,31 @@ def read_words(debug: "DebugLink", do_htc: bool = True) -> list[str]:
debug.press_right()
elif debug.layout_type is LayoutType.Delizia:
debug.swipe_up()
elif debug.layout_type is LayoutType.Eckhart:
debug.click(debug.screen_buttons.ok())
# Swiping through all the pages and loading the words
layout = debug.read_layout()
for _ in range(layout.page_count() - 1):
words.extend(layout.seed_words())
debug.swipe_up()
if debug.layout_type is LayoutType.Eckhart:
debug.click(debug.screen_buttons.ok())
else:
debug.swipe_up()
layout = debug.read_layout()
assert layout is not None
if debug.layout_type in (LayoutType.Bolt, LayoutType.Delizia):
if debug.layout_type in (LayoutType.Bolt, LayoutType.Delizia, LayoutType.Eckhart):
words.extend(layout.seed_words())
if debug.layout_type is LayoutType.Delizia:
debug.swipe_up()
elif debug.layout_type is LayoutType.Eckhart:
debug.click(debug.screen_buttons.ok())
# There is hold-to-confirm button
if do_htc:
if debug.layout_type is LayoutType.Bolt:
if debug.layout_type in (LayoutType.Bolt, LayoutType.Eckhart):
debug.click(debug.screen_buttons.ok(), hold_ms=1500)
elif debug.layout_type is LayoutType.Delizia:
debug.click(debug.screen_buttons.tap_to_confirm())
@ -138,6 +161,12 @@ def read_words(debug: "DebugLink", do_htc: bool = True) -> list[str]:
def confirm_words(debug: "DebugLink", words: list[str]) -> None:
if debug.layout_type is LayoutType.Delizia:
debug.swipe_up()
elif debug.layout_type is LayoutType.Eckhart:
# Press ok if the select word screen is not yet present
if not TR.regexp("reset__select_word_template").match(
debug.read_layout().subtitle()
):
debug.click(debug.screen_buttons.ok())
layout = debug.read_layout()
if debug.layout_type is LayoutType.Bolt:
@ -158,22 +187,6 @@ def confirm_words(debug: "DebugLink", words: list[str]) -> None:
button_pos = btn_texts.index(wanted_word)
debug.click(debug.screen_buttons.word_check_words()[button_pos])
layout = debug.read_layout()
elif debug.layout_type is LayoutType.Delizia:
assert TR.regexp("reset__select_word_x_of_y_template").match(layout.subtitle())
for _ in range(3):
# "Select word 3 of 20"
# ^
word_pos_match = re.search(r"\d+", debug.read_layout().subtitle())
assert word_pos_match is not None
word_pos = int(word_pos_match.group(0))
# Unifying both the buttons and words to lowercase
btn_texts = [
text.lower() for text in layout.tt_check_seed_button_contents()
]
wanted_word = words[word_pos - 1].lower()
button_pos = btn_texts.index(wanted_word)
debug.click(debug.screen_buttons.vertical_menu_items()[button_pos])
layout = debug.read_layout()
elif debug.layout_type is LayoutType.Caesar:
assert TR.reset__select_correct_word in layout.text_content()
debug.press_right()
@ -193,6 +206,43 @@ def confirm_words(debug: "DebugLink", words: list[str]) -> None:
debug.press_middle()
layout = debug.read_layout()
elif debug.layout_type is LayoutType.Delizia:
assert TR.regexp("reset__select_word_x_of_y_template").match(layout.subtitle())
for _ in range(3):
# "Select word 3 of 20"
# ^
word_pos_match = re.search(r"\d+", debug.read_layout().subtitle())
assert word_pos_match is not None
word_pos = int(word_pos_match.group(0))
# Unifying both the buttons and words to lowercase
btn_texts = [
text.lower() for text in layout.tt_check_seed_button_contents()
]
wanted_word = words[word_pos - 1].lower()
button_pos = btn_texts.index(wanted_word)
debug.click(debug.screen_buttons.word_check_words()[button_pos])
layout = debug.read_layout()
elif debug.layout_type is LayoutType.Eckhart:
assert TR.regexp("reset__select_word_template").match(
debug.read_layout().subtitle()
)
for _ in range(3):
# "Select word 3 of 20"
# ^
word_pos_match = re.search(r"\d+", debug.read_layout().subtitle())
assert word_pos_match is not None
word_pos = int(word_pos_match.group(0))
# Unifying both the buttons and words to lowercase
btn_texts = [
text.lower() for text in layout.tt_check_seed_button_contents()
]
wanted_word = words[word_pos - 1].lower()
button_pos = btn_texts.index(wanted_word)
debug.click(debug.screen_buttons.word_check_words()[button_pos])
layout = debug.read_layout()
else:
raise RuntimeError("Unknown model")
def validate_mnemonics(mnemonics: list[str], expected_ems: bytes) -> None:

View File

@ -74,7 +74,7 @@ def set_autolock_delay(device_handler: "BackgroundDeviceHandler", delay_ms: int)
debug.input("1234")
assert TR.regexp("auto_lock__change_template").match(
debug.read_layout().text_content()
debug.read_layout().text_content().strip()
)
layout = go_next(debug)
@ -113,7 +113,7 @@ def test_autolock_interrupts_signing(device_handler: "BackgroundDeviceHandler"):
in debug.read_layout().text_content().replace(" ", "")
)
if debug.layout_type is LayoutType.Bolt:
if debug.layout_type in (LayoutType.Bolt, LayoutType.Eckhart):
debug.click(debug.screen_buttons.ok())
debug.click(debug.screen_buttons.ok())
layout = debug.read_layout()
@ -131,6 +131,8 @@ def test_autolock_interrupts_signing(device_handler: "BackgroundDeviceHandler"):
layout = debug.read_layout()
assert TR.send__total_amount in layout.text_content()
assert "0.0039 BTC" in layout.text_content()
else:
raise ValueError(f"Unsupported layout type: {debug.layout_type}")
# wait for autolock to kick in
time.sleep(10.1)
@ -168,7 +170,7 @@ def test_autolock_does_not_interrupt_signing(device_handler: "BackgroundDeviceHa
in debug.read_layout().text_content().replace(" ", "")
)
if debug.layout_type is LayoutType.Bolt:
if debug.layout_type in (LayoutType.Bolt, LayoutType.Eckhart):
debug.click(debug.screen_buttons.ok())
debug.click(debug.screen_buttons.ok())
layout = debug.read_layout()
@ -187,6 +189,8 @@ def test_autolock_does_not_interrupt_signing(device_handler: "BackgroundDeviceHa
layout = debug.read_layout()
assert TR.send__total_amount in layout.text_content()
assert "0.0039 BTC" in layout.text_content()
else:
raise ValueError(f"Unsupported layout type: {debug.layout_type}")
def sleepy_filter(msg: MessageType) -> MessageType:
time.sleep(10.1)
@ -196,12 +200,14 @@ def test_autolock_does_not_interrupt_signing(device_handler: "BackgroundDeviceHa
with device_handler.client:
device_handler.client.set_filter(messages.TxAck, sleepy_filter)
# confirm transaction
if debug.layout_type is LayoutType.Bolt:
if debug.layout_type in (LayoutType.Bolt, LayoutType.Eckhart):
debug.click(debug.screen_buttons.ok())
elif debug.layout_type is LayoutType.Delizia:
debug.click(debug.screen_buttons.tap_to_confirm())
elif debug.layout_type is LayoutType.Caesar:
debug.press_middle()
elif debug.layout_type is LayoutType.Delizia:
debug.click(debug.screen_buttons.tap_to_confirm())
else:
raise ValueError(f"Unsupported layout type: {debug.layout_type}")
signatures, tx = device_handler.result()
assert len(signatures) == 1
@ -227,7 +233,11 @@ def test_autolock_passphrase_keyboard(device_handler: "BackgroundDeviceHandler")
# enter passphrase - slowly
# keep clicking for long enough to trigger the autolock if it incorrectly ignored key presses
for _ in range(math.ceil(11 / 1.5)):
if debug.layout_type in (LayoutType.Bolt, LayoutType.Delizia):
if debug.layout_type in (
LayoutType.Bolt,
LayoutType.Delizia,
LayoutType.Eckhart,
):
# click at "j"
debug.click(_passphrase_j(debug))
elif debug.layout_type is LayoutType.Caesar:
@ -235,10 +245,13 @@ def test_autolock_passphrase_keyboard(device_handler: "BackgroundDeviceHandler")
# NOTE: because of passphrase randomization it would be a pain to input
# a specific passphrase, which is not in scope for this test.
debug.press_right()
else:
raise ValueError(f"Unsupported layout type: {debug.layout_type}")
time.sleep(1.5)
# Send the passphrase to the client (TT has it clicked already, TR needs to input it)
if debug.layout_type in (LayoutType.Bolt, LayoutType.Delizia):
if debug.layout_type in (LayoutType.Bolt, LayoutType.Delizia, LayoutType.Eckhart):
debug.click(debug.screen_buttons.passphrase_confirm())
elif debug.layout_type is LayoutType.Caesar:
debug.input("j" * 8)
@ -264,15 +277,24 @@ def test_autolock_interrupts_passphrase(device_handler: "BackgroundDeviceHandler
# enter passphrase - slowly
# autolock must activate even if we pressed some buttons
for _ in range(math.ceil(6 / 1.5)):
if debug.layout_type in (LayoutType.Bolt, LayoutType.Delizia):
if debug.layout_type in (
LayoutType.Bolt,
LayoutType.Delizia,
LayoutType.Eckhart,
):
debug.click(_center_button(debug))
elif debug.layout_type is LayoutType.Caesar:
debug.press_middle()
else:
raise ValueError(f"Unsupported layout type: {debug.layout_type}")
time.sleep(1.5)
# wait for autolock to kick in
time.sleep(10.1)
assert debug.read_layout().main_component() == "Lockscreen"
if debug.layout_type is LayoutType.Eckhart:
assert debug.read_layout().main_component() == "Homescreen"
else:
assert debug.read_layout().main_component() == "Lockscreen"
with pytest.raises(exceptions.Cancelled):
device_handler.result()
@ -303,7 +325,10 @@ def test_dryrun_locks_at_number_of_words(device_handler: "BackgroundDeviceHandle
# wait for autolock to trigger
time.sleep(10.1)
assert debug.read_layout().main_component() == "Lockscreen"
if debug.layout_type is LayoutType.Eckhart:
assert debug.read_layout().main_component() == "Homescreen"
else:
assert debug.read_layout().main_component() == "Lockscreen"
with pytest.raises(exceptions.Cancelled):
device_handler.result()
@ -333,17 +358,22 @@ def test_dryrun_locks_at_word_entry(device_handler: "BackgroundDeviceHandler"):
# select 20 words
recovery.select_number_of_words(debug, 20)
if debug.layout_type in (LayoutType.Bolt, LayoutType.Delizia):
if debug.layout_type in (LayoutType.Bolt, LayoutType.Delizia, LayoutType.Eckhart):
layout = go_next(debug)
assert layout.main_component() == "MnemonicKeyboard"
elif debug.layout_type is LayoutType.Caesar:
debug.press_right()
layout = debug.read_layout()
assert "MnemonicKeyboard" in layout.all_components()
else:
raise ValueError(f"Unsupported layout type: {debug.layout_type}")
# make sure keyboard locks
time.sleep(10.1)
assert debug.read_layout().main_component() == "Lockscreen"
if debug.layout_type is LayoutType.Eckhart:
assert debug.read_layout().main_component() == "Homescreen"
else:
assert debug.read_layout().main_component() == "Lockscreen"
with pytest.raises(exceptions.Cancelled):
device_handler.result()
@ -360,7 +390,7 @@ def test_dryrun_enter_word_slowly(device_handler: "BackgroundDeviceHandler"):
# select 20 words
recovery.select_number_of_words(debug, 20)
if debug.layout_type is LayoutType.Bolt:
if debug.layout_type in (LayoutType.Bolt, LayoutType.Eckhart):
debug.click(debug.screen_buttons.ok())
layout = debug.read_layout()
assert layout.main_component() == "MnemonicKeyboard"
@ -409,6 +439,7 @@ def test_dryrun_enter_word_slowly(device_handler: "BackgroundDeviceHandler"):
@pytest.mark.setup_client(pin=PIN4)
@pytest.mark.models(skip=["eckhart"])
def test_autolock_does_not_interrupt_preauthorized(
device_handler: "BackgroundDeviceHandler",
):

View File

@ -80,7 +80,7 @@ def test_backup_slip39_custom(
# confirm backup configuration
if share_count > 1:
assert TR.regexp("reset__create_x_of_y_multi_share_backup_template").match(
debug.read_layout().text_content()
debug.read_layout().text_content().strip()
)
else:
assert TR.regexp("backup__info_single_share_backup").match(
@ -106,9 +106,12 @@ def test_backup_slip39_custom(
all_words.append(" ".join(words))
# confirm backup done
if debug.layout_type is LayoutType.Delizia and share_count > 1:
if (
debug.layout_type in (LayoutType.Delizia, LayoutType.Eckhart)
and share_count > 1
):
reset.confirm_read(debug)
elif debug.layout_type is not LayoutType.Delizia:
elif debug.layout_type not in (LayoutType.Delizia, LayoutType.Eckhart):
reset.confirm_read(debug)
# generate secret locally

View File

@ -49,14 +49,14 @@ def test_hold_to_lock(device_handler: "BackgroundDeviceHandler"):
models.T3B1: 1200,
models.T2T1: 3500,
models.T3T1: 3500,
models.T3W1: 3500,
models.T3W1: 2000,
}[debug.model]
def hold(duration: int) -> None:
if debug.layout_type is LayoutType.Caesar:
debug.press_right(hold_ms=duration)
else:
debug.click((13, 37), hold_ms=duration)
debug.click(debug.screen_buttons.grid34(1, 1), hold_ms=duration)
assert device_handler.features().unlocked is False

View File

@ -31,7 +31,7 @@ if TYPE_CHECKING:
from ..device_handler import BackgroundDeviceHandler
pytestmark = pytest.mark.models("t2t1", "delizia")
pytestmark = pytest.mark.models("t2t1", "delizia", "eckhart")
KEYBOARD_CATEGORIES_BOLT = [
PassphraseCategory.DIGITS,
@ -40,7 +40,8 @@ KEYBOARD_CATEGORIES_BOLT = [
PassphraseCategory.SPECIAL,
]
KEYBOARD_CATEGORIES_DELIZIA = [
# Common for Delizia and Eckhart
KEYBOARD_CATEGORIES_DE = [
PassphraseCategory.LOWERCASE,
PassphraseCategory.UPPERCASE,
PassphraseCategory.DIGITS,
@ -90,8 +91,8 @@ def prepare_passphrase_dialogue(
def keyboard_categories(layout_type: LayoutType) -> list[PassphraseCategory]:
if layout_type is LayoutType.Bolt:
return KEYBOARD_CATEGORIES_BOLT
elif layout_type is LayoutType.Delizia:
return KEYBOARD_CATEGORIES_DELIZIA
elif layout_type in (LayoutType.Delizia, LayoutType.Eckhart):
return KEYBOARD_CATEGORIES_DE
else:
raise ValueError("Wrong layout type")
@ -157,7 +158,7 @@ def enter_passphrase(debug: "DebugLink") -> None:
"""Enter a passphrase"""
is_empty: bool = len(debug.read_layout().passphrase()) == 0
debug.click(debug.screen_buttons.passphrase_confirm())
if is_empty and debug.layout_type is LayoutType.Delizia:
if is_empty and debug.layout_type in (LayoutType.Delizia, LayoutType.Eckhart):
debug.click(debug.screen_buttons.ui_yes())
@ -200,7 +201,7 @@ def test_passphrase_delete(device_handler: "BackgroundDeviceHandler"):
for _ in range(4):
delete_char(debug)
if debug.layout_type is LayoutType.Delizia:
if debug.layout_type in (LayoutType.Delizia, LayoutType.Eckhart):
debug.read_layout()
input_passphrase(debug, CommonPass.SHORT[8 - 4 :])
@ -231,7 +232,7 @@ def test_passphrase_loop_all_characters(device_handler: "BackgroundDeviceHandler
PassphraseCategory.SPECIAL,
):
go_to_category(debug, category)
if debug.layout_type is LayoutType.Delizia:
if debug.layout_type in (LayoutType.Delizia, LayoutType.Eckhart):
debug.read_layout()
enter_passphrase(debug)

View File

@ -95,7 +95,7 @@ def prepare(
# Any action triggering the PIN dialogue
device_handler.run(device.apply_settings, auto_lock_delay_ms=300_000) # type: ignore
tap = True
if situation == Situation.PIN_INPUT_CANCEL:
elif situation == Situation.PIN_INPUT_CANCEL:
# Any action triggering the PIN dialogue
device_handler.run(device.apply_settings, auto_lock_delay_ms=300_000) # type: ignore
elif situation == Situation.PIN_SETUP:
@ -105,13 +105,19 @@ def prepare(
TR.pin__turn_on in debug.read_layout().text_content()
or TR.pin__info in debug.read_layout().text_content()
)
if debug.layout_type in (LayoutType.Bolt, LayoutType.Delizia):
if debug.layout_type in (
LayoutType.Bolt,
LayoutType.Delizia,
LayoutType.Eckhart,
):
go_next(debug)
elif debug.layout_type is LayoutType.Caesar:
go_next(debug)
go_next(debug)
go_next(debug)
go_next(debug)
else:
raise RuntimeError("Unknown model")
elif situation == Situation.PIN_CHANGE:
# Change PIN
device_handler.run(device.change_pin) # type: ignore
@ -153,7 +159,7 @@ def _input_pin(debug: "DebugLink", pin: str, check: bool = False) -> None:
"""Input the PIN"""
if check:
before = debug.read_layout().pin()
if debug.layout_type in (LayoutType.Bolt, LayoutType.Delizia):
if debug.layout_type in (LayoutType.Bolt, LayoutType.Delizia, LayoutType.Eckhart):
digits_order = debug.read_layout().tt_pin_digits_order()
for digit in pin:
digit_index = digits_order.index(digit)
@ -162,6 +168,8 @@ def _input_pin(debug: "DebugLink", pin: str, check: bool = False) -> None:
elif debug.layout_type is LayoutType.Caesar:
for digit in pin:
navigate_to_action_and_press(debug, digit, TR_PIN_ACTIONS)
else:
raise RuntimeError("Unknown model")
if check:
after = debug.read_layout().pin()
@ -170,10 +178,12 @@ def _input_pin(debug: "DebugLink", pin: str, check: bool = False) -> None:
def _see_pin(debug: "DebugLink") -> None:
"""Navigate to "SHOW" and press it"""
if debug.layout_type in (LayoutType.Bolt, LayoutType.Delizia):
if debug.layout_type in (LayoutType.Bolt, LayoutType.Delizia, LayoutType.Eckhart):
debug.click(debug.screen_buttons.pin_passphrase_input())
elif debug.layout_type is LayoutType.Caesar:
navigate_to_action_and_press(debug, SHOW, TR_PIN_ACTIONS)
else:
raise RuntimeError("Unknown model")
def _delete_pin(debug: "DebugLink", digits_to_delete: int, check: bool = True) -> None:
@ -182,10 +192,16 @@ def _delete_pin(debug: "DebugLink", digits_to_delete: int, check: bool = True) -
before = debug.read_layout().pin()
for _ in range(digits_to_delete):
if debug.layout_type in (LayoutType.Bolt, LayoutType.Delizia):
if debug.layout_type in (
LayoutType.Bolt,
LayoutType.Delizia,
LayoutType.Eckhart,
):
debug.click(debug.screen_buttons.pin_passphrase_erase())
elif debug.layout_type is LayoutType.Caesar:
navigate_to_action_and_press(debug, DELETE, TR_PIN_ACTIONS)
else:
raise RuntimeError("Unknown model")
if check:
after = debug.read_layout().pin()
@ -194,13 +210,15 @@ def _delete_pin(debug: "DebugLink", digits_to_delete: int, check: bool = True) -
def _delete_all(debug: "DebugLink", check: bool = True) -> None:
"""Navigate to "DELETE" and hold it until all digits are deleted"""
if debug.layout_type in (LayoutType.Bolt, LayoutType.Delizia):
if debug.layout_type in (LayoutType.Bolt, LayoutType.Delizia, LayoutType.Eckhart):
debug.click(
debug.screen_buttons.pin_passphrase_erase(),
hold_ms=1500,
)
elif debug.layout_type is LayoutType.Caesar:
navigate_to_action_and_press(debug, DELETE, TR_PIN_ACTIONS, hold_ms=1000)
else:
raise RuntimeError("Unknown model")
if check:
after = debug.read_layout().pin()
@ -221,10 +239,12 @@ def _cancel_pin(debug: "DebugLink") -> None:
def _confirm_pin(debug: "DebugLink") -> None:
"""Navigate to "ENTER" and press it"""
if debug.layout_type in (LayoutType.Bolt, LayoutType.Delizia):
if debug.layout_type in (LayoutType.Bolt, LayoutType.Delizia, LayoutType.Eckhart):
debug.click(debug.screen_buttons.pin_confirm())
elif debug.layout_type is LayoutType.Caesar:
navigate_to_action_and_press(debug, ENTER, TR_PIN_ACTIONS)
else:
raise RuntimeError("Unknown model")
def _input_see_confirm(debug: "DebugLink", pin: str) -> None:
@ -326,15 +346,18 @@ def test_pin_setup(device_handler: "BackgroundDeviceHandler"):
def test_pin_setup_mismatch(device_handler: "BackgroundDeviceHandler"):
with PIN_CANCELLED, prepare(device_handler, Situation.PIN_SETUP) as debug:
_enter_two_times(debug, "1", "2")
if debug.layout_type is LayoutType.Bolt:
if debug.layout_type in (
LayoutType.Bolt,
LayoutType.Delizia,
LayoutType.Eckhart,
):
go_next(debug)
_cancel_pin(debug)
elif debug.layout_type is LayoutType.Caesar:
debug.press_middle()
debug.press_no()
elif debug.layout_type is LayoutType.Delizia:
go_next(debug)
_cancel_pin(debug)
else:
raise RuntimeError("Unknown model")
@pytest.mark.setup_client(pin="1")

View File

@ -210,6 +210,8 @@ def read_and_confirm_mnemonic(
mnemonic = yield from read_mnemonic_from_screen_caesar(debug)
elif debug.layout_type is LayoutType.Delizia:
mnemonic = yield from read_mnemonic_from_screen_delizia(debug)
elif debug.layout_type is LayoutType.Eckhart:
mnemonic = yield from read_mnemonic_from_screen_eckhart(debug)
else:
raise ValueError(f"Unknown model: {debug.layout_type}")
@ -277,6 +279,25 @@ def read_mnemonic_from_screen_delizia(
return mnemonic
def read_mnemonic_from_screen_eckhart(
debug: "DebugLink",
) -> Generator[None, "ButtonRequest", list[str]]:
mnemonic: list[str] = []
br = yield
assert br.pages is not None
debug.read_layout()
debug.click(debug.screen_buttons.ok())
for _ in range(br.pages - 2):
words = debug.read_layout().seed_words()
mnemonic.extend(words)
debug.click(debug.screen_buttons.ok())
debug.press_yes()
return mnemonic
def check_share(
debug: "DebugLink", mnemonic: list[str], choose_wrong: bool = False
) -> bool:
@ -294,7 +315,7 @@ def check_share(
elif debug.layout_type is LayoutType.Caesar:
# other models have the instruction in the title/subtitle
word_pos_match = re.search(re_num_of_word, debug.read_layout().title())
elif debug.layout_type is LayoutType.Delizia:
elif debug.layout_type in (LayoutType.Delizia, LayoutType.Eckhart):
word_pos_match = re.search(re_num_of_word, debug.read_layout().subtitle())
else:
word_pos_match = None
@ -319,7 +340,7 @@ def click_info_button_bolt(debug: "DebugLink") -> Generator[Any, Any, ButtonRequ
return (yield)
def click_info_button_delizia(debug: "DebugLink"):
def click_info_button_delizia_eckhart(debug: "DebugLink"):
"""Click Shamir backup info button and return back."""
debug.click(debug.screen_buttons.menu())
layout = debug.read_layout()

View File

@ -178,7 +178,7 @@ class ModelsFilter:
"t1": {models.T1B1},
"t2": {models.T2T1},
"tt": {models.T2T1},
"safe": {models.T2B1, models.T3T1, models.T3B1},
"safe": {models.T2B1, models.T3T1, models.T3B1, models.T3W1},
"safe3": {models.T2B1, models.T3B1},
"safe5": {models.T3T1},
"delizia": {models.T3T1},

View File

@ -27,7 +27,7 @@ BINANCE_PATH = parse_path("m/44h/714h/0h/0/0")
@pytest.mark.altcoin
@pytest.mark.binance
@pytest.mark.models("core")
@pytest.mark.models("core", skip=["eckhart"])
@pytest.mark.setup_client(
mnemonic="offer caution gift cross surge pretty orange during eye soldier popular holiday mention east eight office fashion ill parrot vault rent devote earth cousin"
)

View File

@ -103,7 +103,7 @@ BINANCE_TEST_VECTORS = [
@pytest.mark.altcoin
@pytest.mark.binance
@pytest.mark.models("core")
@pytest.mark.models("core", skip=["eckhart"])
@pytest.mark.setup_client(
mnemonic="offer caution gift cross surge pretty orange during eye soldier popular holiday mention east eight office fashion ill parrot vault rent devote earth cousin"
)

View File

@ -34,6 +34,8 @@ from .signtx import (
request_output,
)
pytestmark = pytest.mark.models(skip=["eckhart"])
B = messages.ButtonRequestType
TX_CACHE_TESTNET = TxCache("Testnet")

View File

@ -153,6 +153,9 @@ VECTORS_DESCRIPTORS = ( # coin, account, script_type, descriptors
)
pytestmark = pytest.mark.models(skip=["eckhart"])
def _address_n(purpose, coin, account, script_type):
res = [H_(purpose), H_(0) if coin == "Bitcoin" else H_(1), H_(account)]
if purpose == 10025 and script_type == messages.InputScriptType.SPENDTAPROOT:

View File

@ -244,7 +244,7 @@ VECTORS_MULTISIG = ( # script_type, bip48_type, address, xpubs, ignore_xpub_mag
)
@pytest.mark.models("core")
@pytest.mark.models("core", skip=["eckhart"])
@pytest.mark.multisig
@pytest.mark.parametrize(
"script_type, bip48_type, address, xpubs, ignore_xpub_magic", VECTORS_MULTISIG

View File

@ -24,6 +24,8 @@ from ...common import is_core
from ...input_flows import InputFlowConfirmAllWarnings
from .signtx import forge_prevtx
pytestmark = pytest.mark.models(skip=["eckhart"])
VECTORS = ( # path, script_types
# GreenAddress A m/[1,4]/address_index
(

View File

@ -35,6 +35,8 @@ from ...input_flows import (
S = messages.InputScriptType
pytestmark = pytest.mark.models(skip=["eckhart"])
def case(
id: str,
@ -406,7 +408,7 @@ def test_signmessage_pagination(client: Client, message: str, is_long: bool):
# We cannot differentiate between a newline and space in the message read from Trezor.
# TODO: do the check also for T2B1
if client.layout_type in (LayoutType.Bolt, LayoutType.Delizia):
if client.layout_type in (LayoutType.Bolt, LayoutType.Delizia, LayoutType.Eckhart):
message_read = IF.message_read.replace(" ", "").replace("...", "")
signed_message = message.replace("\n", "").replace(" ", "")
assert signed_message in message_read

View File

@ -1613,7 +1613,7 @@ def test_information_cancel(client: Client):
@pytest.mark.models(
"core",
skip="delizia",
skip=["delizia", "eckhart"],
reason="Cannot test layouts on T1, not implemented in Delizia UI",
)
def test_information_replacement(client: Client):

View File

@ -33,7 +33,7 @@ PREV_HASH, PREV_TX = forge_prevtx([(INPUT_ADDRESS, 12_300_000)], network="testne
PREV_TXES = {PREV_HASH: PREV_TX}
pytestmark = [pytest.mark.models("core"), pytest.mark.experimental]
pytestmark = [pytest.mark.models("core", skip=["eckhart"]), pytest.mark.experimental]
def case(id, *args, altcoin: bool = False, models: str | None = None):

View File

@ -32,6 +32,8 @@ from .signtx import (
request_output,
)
pytestmark = pytest.mark.models(skip=["eckhart"])
B = messages.ButtonRequestType
TX_CACHE_TESTNET = TxCache("Testnet")

View File

@ -23,6 +23,8 @@ from trezorlib.debuglink import TrezorClientDebugLink as Client
from ...input_flows import InputFlowSignVerifyMessageLong
pytestmark = pytest.mark.models(skip=["eckhart"])
@pytest.mark.models("legacy")
def test_message_long_legacy(client: Client):

View File

@ -14,9 +14,13 @@
# 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>.
import pytest
from trezorlib import btc
from trezorlib.debuglink import TrezorClientDebugLink as Client
pytestmark = pytest.mark.models(skip=["eckhart"])
def test_message_long(client: Client):
ret = btc.verify_message(

View File

@ -14,9 +14,13 @@
# 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>.
import pytest
from trezorlib import btc
from trezorlib.debuglink import TrezorClientDebugLink as Client
pytestmark = pytest.mark.models(skip=["eckhart"])
def test_message_long(client: Client):
ret = btc.verify_message(

View File

@ -32,7 +32,7 @@ from ...input_flows import InputFlowShowXpubQRCode
pytestmark = [
pytest.mark.altcoin,
pytest.mark.cardano,
pytest.mark.models("core"),
pytest.mark.models("core", skip=["eckhart"]),
]

View File

@ -27,7 +27,7 @@ from ...common import MNEMONIC_SLIP39_BASIC_20_3of6
pytestmark = [
pytest.mark.altcoin,
pytest.mark.cardano,
pytest.mark.models("core"),
pytest.mark.models("core", skip=["eckhart"]),
]
ADDRESS_N = parse_path("m/1852h/1815h/0h")

View File

@ -25,7 +25,7 @@ from ...common import parametrize_using_common_fixtures
pytestmark = [
pytest.mark.altcoin,
pytest.mark.cardano,
pytest.mark.models("core"),
pytest.mark.models("core", skip=["eckhart"]),
]

View File

@ -27,7 +27,7 @@ from ...input_flows import InputFlowConfirmAllWarnings
pytestmark = [
pytest.mark.altcoin,
pytest.mark.cardano,
pytest.mark.models("core"),
pytest.mark.models("core", skip=["eckhart"]),
]
@ -40,7 +40,7 @@ def show_details_input_flow(client: Client):
elif client.layout_type is LayoutType.Caesar:
# Caesar - right button for "Show all"
client.debug.press_yes()
elif client.layout_type is LayoutType.Delizia:
elif client.layout_type in (LayoutType.Delizia, LayoutType.Eckhart):
# Delizia - "Show all" button from context menu
client.debug.click(client.debug.screen_buttons.menu())
client.debug.click(client.debug.screen_buttons.vertical_menu_items()[0])

View File

@ -26,7 +26,7 @@ from ...input_flows import InputFlowShowXpubQRCode
@pytest.mark.altcoin
@pytest.mark.eos
@pytest.mark.models("t2t1")
@pytest.mark.models("t2t1", skip=["eckhart"])
@pytest.mark.setup_client(mnemonic=MNEMONIC12)
def test_eos_get_public_key(client: Client):
with client:

View File

@ -29,7 +29,7 @@ ADDRESS_N = parse_path("m/44h/194h/0h/0/0")
pytestmark = [
pytest.mark.altcoin,
pytest.mark.eos,
pytest.mark.models("t2t1"),
pytest.mark.models("t2t1", skip=["eckhart"]),
pytest.mark.setup_client(mnemonic=MNEMONIC12),
]

View File

@ -13,7 +13,11 @@ from ...input_flows import InputFlowConfirmAllWarnings
from . import common
from .test_sign_typed_data import DATA as TYPED_DATA
pytestmark = [pytest.mark.altcoin, pytest.mark.ethereum]
pytestmark = [
pytest.mark.altcoin,
pytest.mark.ethereum,
pytest.mark.models(skip=["eckhart"]),
]
ERC20_OPERATION = "a9059cbb000000000000000000000000574bbb36871ba6b78e27f4b4dcfb76ea0091880b0000000000000000000000000000000000000000000000000000000000000123"
ERC20_BUILTIN_TOKEN = "0xdac17f958d2ee523a2206206994597c13d831ec7" # USDT

View File

@ -13,7 +13,11 @@ from trezorlib.tools import parse_path
from .common import make_defs, make_network, make_payload, make_token, sign_payload
from .test_definitions import DEFAULT_ERC20_PARAMS, ERC20_FAKE_ADDRESS
pytestmark = [pytest.mark.altcoin, pytest.mark.ethereum]
pytestmark = [
pytest.mark.altcoin,
pytest.mark.ethereum,
pytest.mark.models(skip=["eckhart"]),
]
def fails(client: Client, network: bytes, match: str) -> None:

View File

@ -23,7 +23,11 @@ from trezorlib.tools import parse_path
from ...common import parametrize_using_common_fixtures
from ...input_flows import InputFlowShowAddressQRCode
pytestmark = [pytest.mark.altcoin, pytest.mark.ethereum]
pytestmark = [
pytest.mark.altcoin,
pytest.mark.ethereum,
pytest.mark.models(skip=["eckhart"]),
]
@parametrize_using_common_fixtures("ethereum/getaddress.json")

View File

@ -23,7 +23,11 @@ from trezorlib.tools import parse_path
from ...common import parametrize_using_common_fixtures
pytestmark = [pytest.mark.altcoin, pytest.mark.ethereum]
pytestmark = [
pytest.mark.altcoin,
pytest.mark.ethereum,
pytest.mark.models(skip=["eckhart"]),
]
@parametrize_using_common_fixtures("ethereum/getpublickey.json")

View File

@ -23,7 +23,11 @@ from trezorlib.tools import parse_path
from ...common import parametrize_using_common_fixtures
from ...input_flows import InputFlowEIP712Cancel, InputFlowEIP712ShowMore
pytestmark = [pytest.mark.altcoin, pytest.mark.ethereum]
pytestmark = [
pytest.mark.altcoin,
pytest.mark.ethereum,
pytest.mark.models(skip=["eckhart"]),
]
@pytest.mark.models("core")

View File

@ -24,7 +24,11 @@ from trezorlib.tools import parse_path
from ...common import parametrize_using_common_fixtures
from ...input_flows import InputFlowSignVerifyMessageLong
pytestmark = [pytest.mark.altcoin, pytest.mark.ethereum]
pytestmark = [
pytest.mark.altcoin,
pytest.mark.ethereum,
pytest.mark.models(skip=["eckhart"]),
]
@parametrize_using_common_fixtures("ethereum/signmessage.json")

View File

@ -37,7 +37,11 @@ from .common import encode_network
TO_ADDR = "0x1d1c328764a41bda0492b66baa30c4a339ff85ef"
pytestmark = [pytest.mark.altcoin, pytest.mark.ethereum]
pytestmark = [
pytest.mark.altcoin,
pytest.mark.ethereum,
pytest.mark.models(skip=["eckhart"]),
]
def make_defs(parameters: dict) -> messages.EthereumDefinitions:

View File

@ -28,7 +28,10 @@ from ...common import MNEMONIC12
def test_encrypt(client: Client):
def input_flow():
assert (yield).name == "cipher_key_value"
assert client.debug.read_layout().text_content() == TR.misc__enable_labeling
assert (
client.debug.read_layout().text_content().strip()
== TR.misc__enable_labeling
)
client.debug.swipe_up()
client.debug.press_yes()

View File

@ -41,7 +41,7 @@ TEST_VECTORS = [
pytestmark = [
pytest.mark.altcoin,
pytest.mark.monero,
pytest.mark.models("core"),
pytest.mark.models("core", skip=["eckhart"]),
pytest.mark.setup_client(mnemonic=MNEMONIC12),
]

View File

@ -25,7 +25,7 @@ from ...common import MNEMONIC12
@pytest.mark.altcoin
@pytest.mark.monero
@pytest.mark.models("core")
@pytest.mark.models("core", skip=["eckhart"])
@pytest.mark.setup_client(mnemonic=MNEMONIC12)
def test_monero_getwatchkey(client: Client):
res = monero.get_watch_key(client, parse_path("m/44h/128h/0h"))

View File

@ -25,7 +25,7 @@ from ...common import MNEMONIC12
@pytest.mark.altcoin
@pytest.mark.nem
@pytest.mark.models("t1b1", "t2t1")
@pytest.mark.models("t1b1", "t2t1", skip=["eckhart"])
@pytest.mark.setup_client(mnemonic=MNEMONIC12)
@pytest.mark.parametrize("chunkify", (True, False))
def test_nem_getaddress(client: Client, chunkify: bool):

View File

@ -27,7 +27,7 @@ ADDRESS_N = parse_path("m/44h/1h/0h/0h/0h")
pytestmark = [
pytest.mark.altcoin,
pytest.mark.nem,
pytest.mark.models("t1b1", "t2t1"),
pytest.mark.models("t1b1", "t2t1", skip=["eckhart"]),
pytest.mark.setup_client(mnemonic=MNEMONIC12),
]

View File

@ -25,7 +25,7 @@ from ...common import MNEMONIC12
pytestmark = [
pytest.mark.altcoin,
pytest.mark.nem,
pytest.mark.models("t1b1", "t2t1"),
pytest.mark.models("t1b1", "t2t1", skip=["eckhart"]),
pytest.mark.setup_client(mnemonic=MNEMONIC12),
]

View File

@ -25,7 +25,7 @@ from ...common import MNEMONIC12
pytestmark = [
pytest.mark.altcoin,
pytest.mark.nem,
pytest.mark.models("t1b1", "t2t1"),
pytest.mark.models("t1b1", "t2t1", skip=["eckhart"]),
pytest.mark.setup_client(mnemonic=MNEMONIC12),
]

View File

@ -25,7 +25,7 @@ from ...common import MNEMONIC12, is_core
pytestmark = [
pytest.mark.altcoin,
pytest.mark.nem,
pytest.mark.models("t1b1", "t2t1"),
pytest.mark.models("t1b1", "t2t1", skip=["eckhart"]),
pytest.mark.setup_client(mnemonic=MNEMONIC12),
]

View File

@ -22,7 +22,7 @@ import pytest
from trezorlib import messages, nostr
from trezorlib.tools import parse_path
pytestmark = [pytest.mark.altcoin, pytest.mark.models("core")]
pytestmark = [pytest.mark.altcoin, pytest.mark.models("core", skip=["eckhart"])]
# test data from NIP-06: https://github.com/nostr-protocol/nips/blob/master/06.md

View File

@ -34,6 +34,7 @@ from ...input_flows import (
InputFlowSlip39BasicRecoveryInvalidSecondShare,
InputFlowSlip39BasicRecoveryNoAbort,
InputFlowSlip39BasicRecoverySameShare,
InputFlowSlip39BasicRecoveryShareInfoBetweenShares,
InputFlowSlip39BasicRecoveryWrongNthWord,
)
@ -147,6 +148,20 @@ def test_abort_between_shares(client: Client):
assert client.features.recovery_status is messages.RecoveryStatus.Nothing
@pytest.mark.models("eckhart")
@pytest.mark.setup_client(uninitialized=True)
def test_share_info_between_shares(client: Client):
with client:
IF = InputFlowSlip39BasicRecoveryShareInfoBetweenShares(
client, MNEMONIC_SLIP39_BASIC_20_3of6
)
client.set_input_flow(IF.get())
with pytest.raises(exceptions.Cancelled):
device.recover(client, pin_protection=False, label="label")
client.init_device()
assert client.features.initialized is False
@pytest.mark.setup_client(uninitialized=True)
def test_noabort(client: Client):
with client:

View File

@ -255,6 +255,10 @@ def test_already_initialized(client: Client):
def test_entropy_check(client: Client):
with client:
delizia = client.debug.layout_type is LayoutType.Delizia
delizia_eckhart = client.debug.layout_type in (
LayoutType.Delizia,
LayoutType.Eckhart,
)
client.set_expected_responses(
[
messages.ButtonRequest(name="setup_device"),
@ -271,7 +275,7 @@ def test_entropy_check(client: Client):
messages.EntropyCheckReady,
messages.PublicKey,
messages.PublicKey,
(delizia, messages.ButtonRequest(name="backup_device")),
(delizia_eckhart, messages.ButtonRequest(name="backup_device")),
messages.Success,
messages.Features,
]
@ -291,13 +295,17 @@ def test_entropy_check(client: Client):
@pytest.mark.setup_client(uninitialized=True)
def test_no_entropy_check(client: Client):
with client:
delizia_eckhart = client.debug.layout_type in (
LayoutType.Delizia,
LayoutType.Eckhart,
)
delizia = client.debug.layout_type is LayoutType.Delizia
client.set_expected_responses(
[
messages.ButtonRequest(name="setup_device"),
(delizia, messages.ButtonRequest(name="confirm_setup_device")),
messages.EntropyRequest,
(delizia, messages.ButtonRequest(name="backup_device")),
(delizia_eckhart, messages.ButtonRequest(name="backup_device")),
messages.Success,
messages.Features,
]

View File

@ -31,7 +31,7 @@ CUSTOM_MNEMONIC = (
pytestmark = [
pytest.mark.altcoin,
pytest.mark.ripple,
pytest.mark.models("core"),
pytest.mark.models("core", skip=["eckhart"]),
]
# data from https://iancoleman.io/bip39/

View File

@ -24,7 +24,7 @@ from trezorlib.tools import parse_path
pytestmark = [
pytest.mark.altcoin,
pytest.mark.ripple,
pytest.mark.models("core"),
pytest.mark.models("core", skip=["eckhart"]),
]

View File

@ -25,7 +25,7 @@ from ...common import parametrize_using_common_fixtures
pytestmark = [
pytest.mark.altcoin,
pytest.mark.solana,
pytest.mark.models("core"),
pytest.mark.models("core", skip=["eckhart"]),
]

View File

@ -25,7 +25,7 @@ from ...common import parametrize_using_common_fixtures
pytestmark = [
pytest.mark.altcoin,
pytest.mark.solana,
pytest.mark.models("core"),
pytest.mark.models("core", skip=["eckhart"]),
]

View File

@ -29,7 +29,7 @@ from .construct.transaction import Message, RawInstruction
pytestmark = [
pytest.mark.altcoin,
pytest.mark.solana,
pytest.mark.models("core"),
pytest.mark.models("core", skip=["eckhart"]),
]

View File

@ -64,6 +64,7 @@ from ...input_flows import InputFlowShowAddressQRCode
pytestmark = [
pytest.mark.altcoin,
pytest.mark.stellar,
pytest.mark.models(skip=["eckhart"]),
]

View File

@ -9,7 +9,7 @@ from trezorlib.debuglink import TrezorClientDebugLink as Client
from ..common import compact_size
pytestmark = pytest.mark.models("safe")
pytestmark = pytest.mark.models("safe", skip=["eckhart"])
ROOT_PUBLIC_KEY = {
models.T2B1: bytes.fromhex(

View File

@ -23,6 +23,8 @@ from trezorlib.debuglink import LayoutType
from trezorlib.debuglink import TrezorClientDebugLink as Client
from trezorlib.tools import parse_path
pytestmark = pytest.mark.models(skip=["eckhart"])
PIN = "1234"

View File

@ -70,7 +70,7 @@ def test_cancel_message_via_initialize(client: Client, message):
assert isinstance(resp, m.Features)
@pytest.mark.models("core")
@pytest.mark.models("core", skip=["eckhart"])
def test_cancel_on_paginated(client: Client):
"""Check that device is responsive on paginated screen. See #1708."""
# In #1708, the device would ignore USB (or UDP) events while waiting for the user

View File

@ -5,6 +5,8 @@ import pytest
from trezorlib import firmware, models
from trezorlib.debuglink import TrezorClientDebugLink as Client
pytestmark = pytest.mark.models(skip=["eckhart"])
# size of FIRMWARE_AREA, see core/embed/models/model_*_layout.c
FIRMWARE_LENGTHS = {
models.T1B1: 7 * 128 * 1024 + 64 * 1024,

View File

@ -35,7 +35,7 @@ from ..translations import (
sign_blob,
)
pytestmark = pytest.mark.models("core")
pytestmark = pytest.mark.models("core", skip=["eckhart"])
MAX_DATA_LENGTH = {

View File

@ -203,7 +203,7 @@ def test_apply_homescreen_toif(client: Client):
device.apply_settings(client, homescreen=img)
@pytest.mark.models(skip=["legacy", "safe3"])
@pytest.mark.models(skip=["legacy", "safe3", "eckhart"])
def test_apply_homescreen_jpeg(client: Client):
with open(HERE / "test_bg.jpg", "rb") as f:
img = f.read()
@ -325,6 +325,7 @@ def test_apply_homescreen(client: Client):
@pytest.mark.setup_client(pin=None)
@pytest.mark.models(skip="eckhart")
def test_safety_checks(client: Client):
def get_bad_address():
btc.get_address(client, "Bitcoin", parse_path("m/44h"), show_display=True)
@ -423,7 +424,7 @@ def test_label_too_long(client: Client):
device.apply_settings(client, label="A" * 33)
@pytest.mark.models(skip=["legacy", "safe3"])
@pytest.mark.models(skip=["legacy", "safe3", "eckhart"])
@pytest.mark.setup_client(pin=None)
def test_set_brightness(client: Client):
device.set_brightness(client, None)

View File

@ -68,6 +68,8 @@ def test_backup_bip39(client: Client):
def test_backup_slip39_basic(client: Client, click_info: bool):
if click_info and client.layout_type is LayoutType.Caesar:
pytest.skip("click_info not implemented on T2B1")
if click_info and client.layout_type is LayoutType.Eckhart:
pytest.skip("click_info not yet implemented on T3W1")
assert client.features.backup_availability == messages.BackupAvailability.Required
@ -97,7 +99,10 @@ def test_backup_slip39_single(client: Client):
with client:
IF = InputFlowBip39Backup(
client, confirm_success=(client.layout_type is not LayoutType.Delizia)
client,
confirm_success=(
client.layout_type not in (LayoutType.Delizia, LayoutType.Eckhart)
),
)
client.set_input_flow(IF.get())
device.backup(client)
@ -124,6 +129,8 @@ def test_backup_slip39_single(client: Client):
def test_backup_slip39_advanced(client: Client, click_info: bool):
if click_info and client.layout_type is LayoutType.Caesar:
pytest.skip("click_info not implemented on T2B1")
if click_info and client.layout_type is LayoutType.Eckhart:
pytest.skip("click_info not yet implemented on T3W1")
assert client.features.backup_availability == messages.BackupAvailability.Required

View File

@ -23,7 +23,10 @@ from trezorlib.messages import SdProtectOperationType as Op
from ..common import MNEMONIC12
pytestmark = [pytest.mark.models("core", skip="safe3"), pytest.mark.sd_card]
pytestmark = [
pytest.mark.models("core", skip=["safe3", "eckhart"]),
pytest.mark.sd_card,
]
def test_enable_disable(client: Client):

View File

@ -21,7 +21,7 @@ from trezorlib.debuglink import TrezorClientDebugLink as Client
@pytest.mark.setup_client(uninitialized=True)
@pytest.mark.models("safe")
@pytest.mark.models("safe", skip=["eckhart"])
def test_tutorial(client: Client):
device.show_device_tutorial(client)
assert client.features.initialized is False

View File

@ -279,6 +279,7 @@ def test_recovery_device(client: Client):
)
@pytest.mark.models(skip=["eckhart"])
def test_sign_message(client: Client):
_assert_protection(client)
with client:
@ -320,7 +321,7 @@ def test_verify_message_t1(client: Client):
)
@pytest.mark.models("core")
@pytest.mark.models("core", skip=["eckhart"])
def test_verify_message_t2(client: Client):
_assert_protection(client)
with client:

View File

@ -23,7 +23,7 @@ from trezorlib.messages import SdProtectOperationType as Op
from .. import translations as TR
pytestmark = pytest.mark.models("core", skip="safe3")
pytestmark = pytest.mark.models("core", skip=["safe3", "eckhart"])
@pytest.mark.sd_card(formatted=False)

View File

@ -399,7 +399,11 @@ def test_hide_passphrase_from_host(client: Client):
yield
content = client.debug.read_layout().text_content().lower()
assert TR.passphrase__from_host_not_shown[:50].lower() in content
if client.layout_type in (LayoutType.Bolt, LayoutType.Delizia):
if client.layout_type in (
LayoutType.Bolt,
LayoutType.Delizia,
LayoutType.Eckhart,
):
client.debug.press_yes()
elif client.layout_type is LayoutType.Caesar:
client.debug.press_right()

View File

@ -25,7 +25,7 @@ from ...input_flows import InputFlowShowAddressQRCode
pytestmark = [
pytest.mark.altcoin,
pytest.mark.tezos,
pytest.mark.models("core"),
pytest.mark.models("core", skip=["eckhart"]),
]
TEST_VECTORS = [

View File

@ -23,7 +23,7 @@ from trezorlib.tools import parse_path
@pytest.mark.altcoin
@pytest.mark.tezos
@pytest.mark.models("core")
@pytest.mark.models("core", skip=["eckhart"])
def test_tezos_get_public_key(client: Client):
path = parse_path("m/44h/1729h/0h")
pk = get_public_key(client, path)

View File

@ -28,7 +28,7 @@ TEZOS_PATH_15 = parse_path("m/44h/1729h/15h")
pytestmark = [
pytest.mark.altcoin,
pytest.mark.tezos,
pytest.mark.models("core"),
pytest.mark.models("core", skip=["eckhart"]),
]

View File

@ -27,7 +27,7 @@ from .data_webauthn import CRED1, CRED2, CRED3, CREDS
RK_CAPACITY = 100
@pytest.mark.models("core")
@pytest.mark.models("core", skip=["eckhart"])
@pytest.mark.altcoin
@pytest.mark.setup_client(mnemonic=MNEMONIC12)
def test_add_remove(client: Client):

View File

@ -21,6 +21,7 @@ from trezorlib.debuglink import TrezorClientDebugLink as Client
@pytest.mark.altcoin
@pytest.mark.models(skip=["eckhart"])
def test_u2f_counter(client: Client):
assert fido.get_next_counter(client) == 0
assert fido.get_next_counter(client) == 1

View File

@ -50,7 +50,11 @@ TXHASH_4b6cec = bytes.fromhex(
VERSION_GROUP_ID = 0x26A7270A
BRANCH_ID = 0xC2D6D0B4
pytestmark = [pytest.mark.altcoin, pytest.mark.zcash]
pytestmark = [
pytest.mark.altcoin,
pytest.mark.zcash,
pytest.mark.models(skip=["eckhart"]),
]
def test_version_group_id_missing(client: Client):

File diff suppressed because it is too large Load Diff

View File

@ -99,7 +99,7 @@ class RecoveryFlow:
def enter_your_backup(self) -> BRGeneratorType:
assert (yield).name == "recovery"
if self.debug.layout_type is LayoutType.Delizia:
if self.debug.layout_type in (LayoutType.Delizia, LayoutType.Eckhart):
assert TR.recovery__enter_each_word in self._text_content()
else:
assert TR.recovery__enter_backup in self._text_content()
@ -131,7 +131,16 @@ class RecoveryFlow:
def abort_recovery(self, confirm: bool) -> BRGeneratorType:
yield
if self.client.layout_type is LayoutType.Caesar:
if self.client.layout_type is LayoutType.Bolt:
assert TR.recovery__enter_any_share in self._text_content()
self.debug.press_no()
yield
assert TR.recovery__wanna_cancel_recovery in self._text_content()
if confirm:
self.debug.press_yes()
else:
self.debug.press_no()
elif self.client.layout_type is LayoutType.Caesar:
assert TR.recovery__num_of_words in self._text_content()
self.debug.press_no()
yield
@ -141,7 +150,7 @@ class RecoveryFlow:
self.debug.press_yes()
else:
self.debug.press_no()
elif self.client.layout_type is LayoutType.Delizia:
elif self.client.layout_type in (LayoutType.Delizia, LayoutType.Eckhart):
assert TR.recovery__enter_each_word in self._text_content()
self.debug.click(self.debug.screen_buttons.menu())
self.debug.synchronize_at("VerticalMenu")
@ -150,18 +159,19 @@ class RecoveryFlow:
else:
self.debug.click(self.debug.screen_buttons.menu())
else:
assert TR.recovery__enter_any_share in self._text_content()
self.debug.press_no()
yield
assert TR.recovery__wanna_cancel_recovery in self._text_content()
if confirm:
self.debug.press_yes()
else:
self.debug.press_no()
raise ValueError("Unknown model!")
def abort_recovery_between_shares(self) -> BRGeneratorType:
yield
if self.client.layout_type is LayoutType.Caesar:
if self.client.layout_type is LayoutType.Bolt:
assert TR.regexp("recovery__x_of_y_entered_template").search(
self._text_content()
)
self.debug.press_no()
assert (yield).name == "abort_recovery"
assert TR.recovery__wanna_cancel_recovery in self._text_content()
self.debug.press_yes()
elif self.client.layout_type is LayoutType.Caesar:
assert TR.regexp("recovery__x_of_y_entered_template").search(
self._text_content()
)
@ -182,14 +192,36 @@ class RecoveryFlow:
layout = self.debug.read_layout()
assert layout.title() == TR.recovery__title_cancel_recovery
self.debug.click(self.debug.screen_buttons.tap_to_confirm())
else:
elif self.client.layout_type is LayoutType.Eckhart:
assert TR.regexp("recovery__x_of_y_entered_template").search(
self._text_content()
)
self.debug.press_no()
self.debug.click(self.debug.screen_buttons.menu())
self.debug.synchronize_at("VerticalMenu")
self.debug.click(self.debug.screen_buttons.vertical_menu_items()[1])
assert (yield).name == "abort_recovery"
assert TR.recovery__wanna_cancel_recovery in self._text_content()
self.debug.press_yes()
layout = self.debug.read_layout()
assert layout.title() == TR.recovery__title
self.debug.click(self.debug.screen_buttons.ok())
else:
raise ValueError("Unknown model!")
def share_info_between_shares(self) -> BRGeneratorType:
yield
if self.client.layout_type is LayoutType.Eckhart:
assert TR.regexp("recovery__x_of_y_entered_template").search(
self._text_content()
)
self.debug.click(self.debug.screen_buttons.menu())
self.debug.synchronize_at("VerticalMenu")
self.debug.click(self.debug.screen_buttons.vertical_menu_items()[0])
assert (yield).name == "recovery_share"
layout = self.debug.read_layout()
assert layout.title() == TR.words__recovery_share
self.debug.click(self.debug.screen_buttons.menu())
self.debug.click(self.debug.screen_buttons.menu())
else:
raise ValueError("Unsupported model!")
def input_number_of_words(self, num_words: int | None) -> BRGeneratorType:
br = yield
@ -321,10 +353,11 @@ class RecoveryFlow:
if click_info:
if self.client.layout_type is LayoutType.Bolt:
yield from self.click_info_bolt()
elif self.client.layout_type is LayoutType.Delizia:
yield from self.click_info_delizia()
else:
raise ValueError("Unknown model!")
elif self.client.layout_type in (
LayoutType.Delizia,
LayoutType.Eckhart,
):
yield from self.click_info_delizia_eckhart()
yield from self.success_more_shares_needed()
def click_info_bolt(self) -> t.Generator[t.Any, t.Any, None]:
@ -335,7 +368,7 @@ class RecoveryFlow:
self.debug.swipe_up()
self.debug.press_yes()
def click_info_delizia(self) -> BRGeneratorType:
def click_info_delizia_eckhart(self) -> BRGeneratorType:
# Moving through the menu into the show_shares screen
self.debug.click(self.debug.screen_buttons.menu())
self.debug.synchronize_at("VerticalMenu")
@ -550,6 +583,31 @@ class EthereumFlow:
self.debug.press_yes()
elif self.client.layout_type is LayoutType.Caesar:
# confirm intro
if info:
self.debug.press_right()
assert self.debug.read_layout().title() in (
TR.ethereum__staking_stake_address,
TR.ethereum__staking_claim_address,
)
self.debug.press_left()
self.debug.press_middle()
yield
# confirm summary
if info:
self.debug.press_right()
assert TR.ethereum__gas_limit in self.debug.read_layout().text_content()
self.debug.press_right()
assert TR.ethereum__gas_price in self.debug.read_layout().text_content()
self.debug.press_left()
self.debug.press_left()
self.debug.press_middle()
yield
self.debug.press_yes()
elif self.client.layout_type is LayoutType.Delizia:
# confirm intro
if info:
@ -582,30 +640,5 @@ class EthereumFlow:
self.debug.press_yes()
elif self.client.layout_type is LayoutType.Caesar:
# confirm intro
if info:
self.debug.press_right()
assert self.debug.read_layout().title() in (
TR.ethereum__staking_stake_address,
TR.ethereum__staking_claim_address,
)
self.debug.press_left()
self.debug.press_middle()
yield
# confirm summary
if info:
self.debug.press_right()
assert TR.ethereum__gas_limit in self.debug.read_layout().text_content()
self.debug.press_right()
assert TR.ethereum__gas_price in self.debug.read_layout().text_content()
self.debug.press_left()
self.debug.press_left()
self.debug.press_middle()
yield
self.debug.press_yes()
else:
raise ValueError("Unknown model!")

View File

@ -40,7 +40,7 @@ def test_abort(core_emulator: Emulator):
debug = device_handler.debuglink()
features = device_handler.features()
if debug.layout_type is LayoutType.Delizia:
if debug.layout_type in (LayoutType.Delizia, LayoutType.Eckhart):
pytest.skip("abort not supported on T3T1")
assert features.recovery_status == RecoveryStatus.Nothing
@ -187,7 +187,7 @@ def test_recovery_multiple_resets(core_emulator: Emulator):
shares = MNEMONIC_SLIP39_ADVANCED_20
layout = debug.read_layout()
expected_text = "Enter any share"
if debug.layout_type == LayoutType.Delizia:
if debug.layout_type in (LayoutType.Delizia, LayoutType.Eckhart):
expected_text = "Enter each word"
remaining = len(shares)
for share in shares:

File diff suppressed because it is too large Load Diff