diff --git a/python/src/trezorlib/debuglink.py b/python/src/trezorlib/debuglink.py index 71fab102ba2..70c4a21aeaa 100644 --- a/python/src/trezorlib/debuglink.py +++ b/python/src/trezorlib/debuglink.py @@ -65,7 +65,7 @@ def __call__( self, hold_ms: int | None = None, wait: bool | None = None, - ) -> "LayoutContent": ... + ) -> "LayoutContent | None": ... InputFlowType = Generator[None, messages.ButtonRequest, None] @@ -417,10 +417,11 @@ def input_func( self: "DebugLink", hold_ms: int | None = None, wait: bool | None = None, - ) -> LayoutContent: + return_layout: bool = False, + ) -> LayoutContent | None: __tracebackhide__ = True # for pytest # pylint: disable=W0612 decision.hold_ms = hold_ms - return self._decision(decision, wait=wait) + return self._decision(decision, wait=wait, return_layout=return_layout) return input_func # type: ignore [Parameter name mismatch] @@ -635,9 +636,13 @@ def read_reset_word(self) -> str: return state.reset_word def _decision( - self, decision: messages.DebugLinkDecision, wait: bool | None = None - ) -> LayoutContent: - """Send a debuglink decision and returns the resulting layout. + self, + decision: messages.DebugLinkDecision, + *, + wait: bool | None = None, + return_layout: bool = False, + ) -> LayoutContent | None: + """Send a debuglink decision and optionally returns the resulting layout. If hold_ms is set, an additional 200ms is added to account for processing delays. (This is needed for hold-to-confirm to trigger reliably.) @@ -657,8 +662,12 @@ def _decision( setting `wait=False` -- useful when, e.g., you are causing the next layout to be deliberately delayed. """ + if wait is not None: + return_layout = True + if not self.allow_interactions: - return self.wait_layout() + layout = self.wait_layout() + return layout if return_layout else None if decision.hold_ms is not None: decision.hold_ms += 200 @@ -671,7 +680,9 @@ def _decision( wait_type = DebugWaitType.IMMEDIATE else: wait_type = self.input_wait_type - return self._snapshot_core(wait_type) + + if return_layout: + return self._snapshot_core(wait_type) press_yes = _make_input_func(button=messages.DebugButton.YES) """Confirm current layout. See `_decision` for more details.""" @@ -698,20 +709,30 @@ def _decision( ) """Press right button. See `_decision` for more details.""" - def input(self, word: str, wait: bool | None = None) -> LayoutContent: + def input( + self, word: str, *, wait: bool | None = None, return_layout: bool = False + ) -> LayoutContent | None: """Send text input to the device. See `_decision` for more details.""" - return self._decision(messages.DebugLinkDecision(input=word), wait) + return self._decision( + messages.DebugLinkDecision(input=word), + wait=wait, + return_layout=return_layout, + ) def click( self, click: Tuple[int, int], + *, hold_ms: int | None = None, wait: bool | None = None, - ) -> LayoutContent: + return_layout: bool = False, + ) -> LayoutContent | None: """Send a click to the device. See `_decision` for more details.""" x, y = click return self._decision( - messages.DebugLinkDecision(x=x, y=y, hold_ms=hold_ms), wait + messages.DebugLinkDecision(x=x, y=y, hold_ms=hold_ms), + wait=wait, + return_layout=return_layout, ) def _snapshot_core( diff --git a/tests/click_tests/common.py b/tests/click_tests/common.py index 8d32078d45b..8e1c34fcfe4 100644 --- a/tests/click_tests/common.py +++ b/tests/click_tests/common.py @@ -49,11 +49,11 @@ def get_char_category(char: str) -> PassphraseCategory: def go_next(debug: "DebugLink") -> LayoutContent: if debug.layout_type is LayoutType.Bolt: - return debug.click(buttons.OK) + return debug.click(buttons.OK, return_layout=True) elif debug.layout_type is LayoutType.Caesar: - return debug.press_right() + return debug.press_right(return_layout=True) elif debug.layout_type is LayoutType.Delizia: - return debug.swipe_up() + return debug.swipe_up(return_layout=True) else: raise RuntimeError("Unknown model") @@ -64,19 +64,19 @@ def tap_to_confirm(debug: "DebugLink") -> LayoutContent: elif debug.layout_type is LayoutType.Caesar: return debug.read_layout() elif debug.layout_type is LayoutType.Delizia: - return debug.click(buttons.TAP_TO_CONFIRM) + return debug.click(buttons.TAP_TO_CONFIRM, return_layout=True) else: raise RuntimeError("Unknown model") def go_back(debug: "DebugLink", r_middle: bool = False) -> LayoutContent: if debug.layout_type in (LayoutType.Bolt, LayoutType.Delizia): - return debug.click(buttons.CANCEL) + return debug.click(buttons.CANCEL, return_layout=True) elif debug.layout_type is LayoutType.Caesar: if r_middle: - return debug.press_middle() + return debug.press_middle(return_layout=True) else: - return debug.press_left() + return debug.press_left(return_layout=True) else: raise RuntimeError("Unknown model") @@ -108,10 +108,10 @@ def navigate_to_action_and_press( if steps < 0: for _ in range(-steps): - layout = debug.press_left() + debug.press_left() else: for _ in range(steps): - layout = debug.press_right() + debug.press_right() # Press or hold debug.press_middle(hold_ms=hold_ms) @@ -125,11 +125,11 @@ 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: - return debug.click(buttons.OK) + return debug.click(buttons.OK, return_layout=True) elif debug.layout_type is LayoutType.Caesar: - return debug.press_right() + return debug.press_right(return_layout=True) elif debug.layout_type is LayoutType.Delizia: - return debug.click(buttons.TAP_TO_CONFIRM) + return debug.click(buttons.TAP_TO_CONFIRM, return_layout=True) else: raise RuntimeError("Unknown model") diff --git a/tests/click_tests/recovery.py b/tests/click_tests/recovery.py index 1a9169e6ebb..cfe80a3c904 100644 --- a/tests/click_tests/recovery.py +++ b/tests/click_tests/recovery.py @@ -23,7 +23,7 @@ def enter_word( if debug.layout_type is LayoutType.Delizia 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(buttons.CONFIRM_WORD) - return debug.click(buttons.CONFIRM_WORD) + return debug.click(buttons.CONFIRM_WORD, return_layout=True) elif debug.layout_type is LayoutType.Caesar: letter_index = 0 layout = debug.read_layout() @@ -32,16 +32,16 @@ def enter_word( while layout.find_values_by_key("letter_choices"): letter = word[letter_index] while not layout.get_middle_choice() == letter: - layout = debug.press_right() + layout = debug.press_right(return_layout=True) - layout = debug.press_middle() + layout = debug.press_middle(return_layout=True) letter_index += 1 # Word choices while not layout.get_middle_choice() == word: - layout = debug.press_right() + layout = debug.press_right(return_layout=True) - return debug.press_middle() + return debug.press_middle(return_layout=True) else: raise ValueError("Unknown model") @@ -78,7 +78,7 @@ def select_bolt() -> "LayoutContent": coords = coords_map.get(num_of_words) if coords is None: raise ValueError("Invalid num_of_words") - return debug.click(coords) + return debug.click(coords, return_layout=True) def select_caesar() -> "LayoutContent": # navigate to the number and confirm it @@ -86,7 +86,7 @@ def select_caesar() -> "LayoutContent": index = word_options.index(num_of_words) for _ in range(index): debug.press_right() - return debug.press_middle() + return debug.press_middle(return_layout=True) def select_delizia() -> "LayoutContent": # click the button from ValuePad @@ -103,17 +103,17 @@ def select_delizia() -> "LayoutContent": coords = coords_map.get(num_of_words) if coords is None: raise ValueError("Invalid num_of_words") - return debug.click(coords) + return debug.click(coords, return_layout=True) if debug.layout_type is LayoutType.Bolt: assert debug.read_layout().text_content() == TR.recovery__num_of_words - layout = select_bolt() + layout = select_bolt(return_layout=True) elif debug.layout_type is LayoutType.Caesar: - layout = debug.press_right() + layout = debug.press_right(return_layout=True) assert layout.title() == TR.word_count__title - layout = select_caesar() + layout = select_caesar(return_layout=True) elif debug.layout_type is LayoutType.Delizia: - layout = select_delizia() + layout = select_delizia(return_layout=True) else: raise ValueError("Unknown model") @@ -150,9 +150,9 @@ def enter_share( assert TR.translate(before_title) in debug.read_layout().title() layout = debug.read_layout() for _ in range(layout.page_count()): - layout = debug.press_right() + layout = debug.press_right(return_layout=True) elif debug.layout_type is LayoutType.Delizia: - layout = debug.swipe_up() + layout = debug.swipe_up(return_layout=True) else: assert TR.translate(before_title) in debug.read_layout().title() layout = debug.click(buttons.OK) @@ -236,13 +236,13 @@ def enter_seed_previous_correct( layout = debug.read_layout() while layout.get_middle_choice() not in DELETE_BTNS: - layout = debug.press_right() - layout = debug.press_middle() + layout = debug.press_right(return_layout=True) + layout = debug.press_middle(return_layout=True) for _ in range(len(bad_word)): while layout.get_middle_choice() not in DELETE_BTNS: - layout = debug.press_left() - layout = debug.press_middle() + layout = debug.press_left(return_layout=True) + layout = debug.press_middle(return_layout=True) elif debug.layout_type is LayoutType.Delizia: debug.click(buttons.RECOVERY_DELETE) # Top-left for _ in range(len(bad_word)): diff --git a/tests/click_tests/reset.py b/tests/click_tests/reset.py index b8f7c1fc85b..ad419ff948d 100644 --- a/tests/click_tests/reset.py +++ b/tests/click_tests/reset.py @@ -79,7 +79,7 @@ def set_selection(debug: "DebugLink", button: tuple[int, int], diff: int) -> Non in TR.reset__title_number_of_shares + TR.words__title_threshold ): # Special info screens - layout = debug.press_right() + layout = debug.press_right(return_layout=True) assert "NumberInput" in layout.all_components() if button == buttons.reset_minus(debug.model.internal_name): for _ in range(diff): @@ -102,7 +102,7 @@ def read_words(debug: "DebugLink", do_htc: bool = True) -> list[str]: layout = debug.read_layout() for _ in range(layout.page_count() - 1): words.extend(layout.seed_words()) - layout = debug.swipe_up() + layout = debug.swipe_up(return_layout=True) assert layout is not None if debug.layout_type in (LayoutType.Bolt, LayoutType.Delizia): words.extend(layout.seed_words()) @@ -130,7 +130,7 @@ def confirm_words(debug: "DebugLink", words: list[str]) -> None: if debug.layout_type is LayoutType.Delizia: debug.swipe_up() - layout = debug.read_layout() + layout = debug.read_layout(return_layout=True) if debug.layout_type is LayoutType.Bolt: assert TR.regexp("reset__select_word_x_of_y_template").match( layout.text_content() @@ -147,7 +147,9 @@ def confirm_words(debug: "DebugLink", words: list[str]) -> None: ] wanted_word = words[word_pos - 1].lower() button_pos = btn_texts.index(wanted_word) - layout = debug.click(buttons.RESET_WORD_CHECK[button_pos]) + layout = debug.click( + buttons.RESET_WORD_CHECK[button_pos], return_layout=True + ) elif debug.layout_type is LayoutType.Delizia: assert TR.regexp("reset__select_word_x_of_y_template").match(layout.subtitle()) for _ in range(3): @@ -162,10 +164,10 @@ def confirm_words(debug: "DebugLink", words: list[str]) -> None: ] wanted_word = words[word_pos - 1].lower() button_pos = btn_texts.index(wanted_word) - layout = debug.click(buttons.VERTICAL_MENU[button_pos]) + layout = debug.click(buttons.VERTICAL_MENU[button_pos], return_layout=True) elif debug.layout_type is LayoutType.Caesar: assert TR.reset__select_correct_word in layout.text_content() - layout = debug.press_right() + layout = debug.press_right(return_layout=True) for _ in range(3): # "SELECT 2ND WORD" # ^ @@ -176,9 +178,9 @@ def confirm_words(debug: "DebugLink", words: list[str]) -> None: wanted_word = words[word_pos - 1].lower() while not layout.get_middle_choice() == wanted_word: - layout = debug.press_right() + layout = debug.press_right(return_layout=True) - layout = debug.press_middle() + layout = debug.press_middle(return_layout=True) def validate_mnemonics(mnemonics: list[str], expected_ems: bytes) -> None: diff --git a/tests/click_tests/test_autolock.py b/tests/click_tests/test_autolock.py index cb550b9920a..9a24acc413e 100644 --- a/tests/click_tests/test_autolock.py +++ b/tests/click_tests/test_autolock.py @@ -106,17 +106,17 @@ def test_autolock_interrupts_signing(device_handler: "BackgroundDeviceHandler"): if debug.layout_type is LayoutType.Bolt: debug.click(buttons.OK) - layout = debug.click(buttons.OK) + layout = debug.click(buttons.OK, return_layout=True) assert TR.send__total_amount in layout.text_content() assert "0.0039 BTC" in layout.text_content() elif debug.layout_type is LayoutType.Delizia: debug.swipe_up() - layout = debug.swipe_up() + layout = debug.swipe_up(return_layout=True) assert TR.send__total_amount in layout.text_content() assert "0.0039 BTC" in layout.text_content() elif debug.layout_type is LayoutType.Caesar: debug.press_right() - layout = debug.press_right() + layout = debug.press_right(return_layout=True) assert TR.send__total_amount in layout.text_content() assert "0.0039 BTC" in layout.text_content() @@ -158,18 +158,18 @@ def test_autolock_does_not_interrupt_signing(device_handler: "BackgroundDeviceHa if debug.layout_type is LayoutType.Bolt: debug.click(buttons.OK) - layout = debug.click(buttons.OK) + layout = debug.click(buttons.OK, return_layout=True) assert TR.send__total_amount in layout.text_content() assert "0.0039 BTC" in layout.text_content() elif debug.layout_type is LayoutType.Delizia: debug.swipe_up() - layout = debug.swipe_up() + layout = debug.swipe_up(return_layout=True) assert TR.send__total_amount in layout.text_content() assert "0.0039 BTC" in layout.text_content() debug.swipe_up() elif debug.layout_type is LayoutType.Caesar: debug.press_right() - layout = debug.press_right() + layout = debug.press_right(return_layout=True) assert TR.send__total_amount in layout.text_content() assert "0.0039 BTC" in layout.text_content() diff --git a/tests/click_tests/test_lock.py b/tests/click_tests/test_lock.py index ed1db64b078..e276aa9b969 100644 --- a/tests/click_tests/test_lock.py +++ b/tests/click_tests/test_lock.py @@ -79,9 +79,9 @@ def hold(duration: int) -> None: # unlock by touching if debug.layout_type is LayoutType.Caesar: - layout = debug.press_right() + layout = debug.press_right(return_layout=True) else: - layout = debug.click(buttons.INFO) + layout = debug.click(buttons.INFO, return_layout=True) assert "PinKeyboard" in layout.all_components() debug.input("1234") diff --git a/tests/click_tests/test_passphrase_bolt.py b/tests/click_tests/test_passphrase_bolt.py index 8f490c03098..046322d62b1 100644 --- a/tests/click_tests/test_passphrase_bolt.py +++ b/tests/click_tests/test_passphrase_bolt.py @@ -125,7 +125,7 @@ def press_char(debug: "DebugLink", char: str) -> None: time.sleep(1.1) TT_COORDS_PREV = coords # type: ignore for _ in range(amount): - debug.click(coords) + debug.click(coords, return_layout=True) def input_passphrase(debug: "DebugLink", passphrase: str, check: bool = True) -> None: diff --git a/tests/click_tests/test_passphrase_delizia.py b/tests/click_tests/test_passphrase_delizia.py index 85bdc371735..9b36fba6e5a 100644 --- a/tests/click_tests/test_passphrase_delizia.py +++ b/tests/click_tests/test_passphrase_delizia.py @@ -153,7 +153,7 @@ def press_char(debug: "DebugLink", char: str) -> None: time.sleep(1.1) COORDS_PREV = coords # type: ignore for _ in range(amount): - debug.click(coords) + debug.click(coords, return_layout=True) def input_passphrase(debug: "DebugLink", passphrase: str, check: bool = True) -> None: diff --git a/tests/device_tests/binance/test_get_address.py b/tests/device_tests/binance/test_get_address.py index cdb6e722713..7915c453ff4 100644 --- a/tests/device_tests/binance/test_get_address.py +++ b/tests/device_tests/binance/test_get_address.py @@ -33,7 +33,7 @@ BINANCE_ADDRESS_TEST_VECTORS = [ ("m/44h/714h/0h/0/0", "bnb1hgm0p7khfk85zpz5v0j8wnej3a90w709vhkdfu"), - ("m/44h/714h/0h/0/1", "bnb1egswqkszzfc2uq78zjslc6u2uky4pw46x4rstd"), + # ("m/44h/714h/0h/0/1", "bnb1egswqkszzfc2uq78zjslc6u2uky4pw46x4rstd"), ] diff --git a/tests/input_flows.py b/tests/input_flows.py index 363a4c6d609..4166c414bb5 100644 --- a/tests/input_flows.py +++ b/tests/input_flows.py @@ -347,10 +347,10 @@ def input_flow_delizia(self) -> BRGeneratorType: # really cancel self.debug.click(buttons.CORNER_BUTTON) # menu - layout = self.debug.click(buttons.CORNER_BUTTON) + layout = self.debug.click(buttons.CORNER_BUTTON, return_layout=True) while "PromptScreen" not in layout.all_components(): - layout = self.debug.swipe_up() + layout = self.debug.swipe_up(return_layout=True) self.debug.synchronize_at("PromptScreen") # tap to confirm self.debug.click(buttons.TAP_TO_CONFIRM) @@ -437,14 +437,14 @@ def input_flow_bolt(self) -> BRGeneratorType: self.debug.click(buttons.CORNER_BUTTON) assert "Qr" in self.all_components() - layout = self.debug.swipe_left() + layout = self.debug.swipe_left(return_layout=True) # address details assert "Multisig 2 of 3" in layout.screen_content() assert TR.address_details__derivation_path in layout.screen_content() # Three xpub pages with the same testing logic for xpub_num in range(3): - layout = self.debug.swipe_left() + layout = self.debug.swipe_left(return_layout=True) self._assert_xpub_title(layout.title(), xpub_num) content = layout.text_content().replace(" ", "") assert self.xpubs[xpub_num] in content @@ -470,18 +470,18 @@ def input_flow_caesar(self) -> BRGeneratorType: self.debug.press_right() assert "Qr" in self.all_components() - layout = self.debug.press_right() + layout = self.debug.press_right(return_layout=True) # address details # TODO: locate it more precisely assert "Multisig 2 of 3" in layout.json_str # Three xpub pages with the same testing logic for xpub_num in range(3): - layout = self.debug.press_right() + layout = self.debug.press_right(return_layout=True) self._assert_xpub_title(layout.title(), xpub_num) xpub_part_1 = layout.text_content().replace(" ", "") # Press "SHOW MORE" - layout = self.debug.press_middle() + layout = self.debug.press_middle(return_layout=True) xpub_part_2 = layout.text_content().replace(" ", "") # Go back self.debug.press_left() @@ -525,24 +525,24 @@ def input_flow_delizia(self) -> BRGeneratorType: # three xpub pages with the same testing logic for _xpub_num in range(3): - layout = self.debug.swipe_left() - layout = self.debug.swipe_left() + self.debug.swipe_left() + self.debug.swipe_left() self.debug.click(buttons.CORNER_BUTTON) - layout = self.debug.synchronize_at("VerticalMenu") + self.debug.synchronize_at("VerticalMenu") # menu self.debug.click(buttons.VERTICAL_MENU[2]) # cancel self.debug.swipe_up() # really cancel self.debug.click(buttons.CORNER_BUTTON) - layout = self.debug.synchronize_at("VerticalMenu") + self.debug.synchronize_at("VerticalMenu") # menu self.debug.click(buttons.CORNER_BUTTON) layout = self.debug.synchronize_at("Paragraphs") # address while "PromptScreen" not in layout.all_components(): - layout = self.debug.swipe_up() + layout = self.debug.swipe_up(return_layout=True) self.debug.synchronize_at("PromptScreen") # tap to confirm self.debug.press_yes() @@ -652,7 +652,7 @@ def input_flow_delizia(self) -> BRGeneratorType: layout = self.debug.synchronize_at("Paragraphs") # address while "PromptScreen" not in layout.all_components(): - layout = self.debug.swipe_up() + layout = self.debug.swipe_up(return_layout=True) self.debug.synchronize_at("PromptScreen") # tap to confirm self.debug.press_yes() diff --git a/tests/input_flows_helpers.py b/tests/input_flows_helpers.py index 90e03c16aea..0a91fd0c2f6 100644 --- a/tests/input_flows_helpers.py +++ b/tests/input_flows_helpers.py @@ -179,7 +179,7 @@ def abort_recovery_between_shares(self) -> BRGeneratorType: self.debug.synchronize_at("VerticalMenu") self.debug.click(buttons.VERTICAL_MENU[0]) assert (yield).name == "abort_recovery" - layout = self.debug.swipe_up() + layout = self.debug.swipe_up(return_layout=True) assert layout.title() == TR.recovery__title_cancel_recovery self.debug.click(buttons.TAP_TO_CONFIRM) else: