WIP - new approach to debug events for both TT and TR

grdddj/debuglink_improvements
grdddj 1 year ago
parent 67d87fab94
commit b973d3c692

@ -11,7 +11,7 @@ if __debug__:
from trezor import log, loop, utils, wire from trezor import log, loop, utils, wire
from trezor.ui import display from trezor.ui import display
from trezor.enums import MessageType, DebugPhysicalButton from trezor.enums import MessageType
from trezor.messages import ( from trezor.messages import (
DebugLinkLayout, DebugLinkLayout,
Success, Success,
@ -33,20 +33,16 @@ if __debug__:
DebugLinkWatchLayout, DebugLinkWatchLayout,
) )
reset_current_words = loop.chan()
reset_word_index = loop.chan() reset_word_index = loop.chan()
confirm_chan = loop.chan()
swipe_chan = loop.chan() swipe_chan = loop.chan()
input_chan = loop.chan() result_chan = loop.chan()
model_r_btn_chan = loop.chan() button_chan = loop.chan()
confirm_signal = confirm_chan.take click_chan = loop.chan()
swipe_signal = swipe_chan.take swipe_signal = swipe_chan.take
input_signal = input_chan.take result_signal = result_chan.take
model_r_btn_signal = model_r_btn_chan.take button_signal = button_chan.take
click_signal = click_chan.take
synthetic_event = loop.chan()
synthetic_event_signal = synthetic_event.take
debuglink_decision_chan = loop.chan() debuglink_decision_chan = loop.chan()
@ -74,21 +70,18 @@ if __debug__:
event_id: int | None, msg: DebugLinkDecision event_id: int | None, msg: DebugLinkDecision
) -> None: ) -> None:
from trezor.enums import DebugButton from trezor.enums import DebugButton
from trezor.ui import Result
if msg.button is not None: if msg.button is not None:
if msg.button == DebugButton.NO: if msg.button == DebugButton.NO:
await confirm_chan.put(Result(trezorui2.CANCELLED)) await result_chan.put((event_id, trezorui2.CANCELLED))
elif msg.button == DebugButton.YES: elif msg.button == DebugButton.YES:
await confirm_chan.put(Result(trezorui2.CONFIRMED)) await result_chan.put((event_id, trezorui2.CONFIRMED))
elif msg.button == DebugButton.INFO: elif msg.button == DebugButton.INFO:
await confirm_chan.put(Result(trezorui2.INFO)) await result_chan.put((event_id, trezorui2.INFO))
if msg.physical_button is not None: if msg.input is not None:
await model_r_btn_chan.put(msg.physical_button) await result_chan.put((event_id, msg.input))
if msg.swipe is not None: if msg.swipe is not None:
await swipe_chan.put((event_id, msg.swipe)) await swipe_chan.put((event_id, msg.swipe))
if msg.input is not None:
await input_chan.put(Result(msg.input))
async def debuglink_decision_dispatcher() -> None: async def debuglink_decision_dispatcher() -> None:
while True: while True:
@ -105,12 +98,12 @@ if __debug__:
while True: while True:
event_id, content = await layout_change_chan.take() event_id, content = await layout_change_chan.take()
if event_id is None or awaited_event_id is None: if awaited_event_id is None or event_id is None:
# Not waiting for anything or event does not have ID # Not waiting for anything or event does not have ID
break break
elif event_id == awaited_event_id: elif event_id == awaited_event_id:
# We found what we were waiting for # We found what we were waiting for
storage.awaited_event = None debug_events.awaited_event = None
break break
elif event_id > awaited_event_id: elif event_id > awaited_event_id:
# Sanity check # Sanity check
@ -136,21 +129,6 @@ if __debug__:
await DEBUG_CONTEXT.write(DebugLinkState(layout_lines=content)) await DEBUG_CONTEXT.write(DebugLinkState(layout_lines=content))
storage.layout_watcher = LAYOUT_WATCHER_NONE storage.layout_watcher = LAYOUT_WATCHER_NONE
async def touch_hold(
event_id: int | None, x: int, y: int, duration_ms: int
) -> None:
from trezor import io
await loop.sleep(duration_ms)
event = (event_id, io.TOUCH_END)
synthetic_event.publish((event, x, y))
async def button_hold(btn: int, duration_ms: int) -> None:
from trezor import io
await loop.sleep(duration_ms)
synthetic_event.publish((io.BUTTON, (io.BUTTON_RELEASED, btn)))
async def dispatch_DebugLinkWatchLayout( async def dispatch_DebugLinkWatchLayout(
ctx: wire.Context, msg: DebugLinkWatchLayout ctx: wire.Context, msg: DebugLinkWatchLayout
) -> Success: ) -> Success:
@ -161,7 +139,10 @@ if __debug__:
# analyze the resulted layout. # analyze the resulted layout.
# Resetting the debug events makes sure that the previous # Resetting the debug events makes sure that the previous
# events/layouts are not mixed with the new ones. # events/layouts are not mixed with the new ones.
storage.reset_debug_events() # TODO: create a special flag for resetting this (DebugLinkWatchLayout.reset_debug_events)
# OR a custom message - DebugLinkResetDebugEvents
if not msg.watch:
storage.reset_debug_events()
layout_change_chan.putters.clear() layout_change_chan.putters.clear()
if msg.watch: if msg.watch:
@ -173,7 +154,7 @@ if __debug__:
async def dispatch_DebugLinkDecision( async def dispatch_DebugLinkDecision(
ctx: wire.Context, msg: DebugLinkDecision ctx: wire.Context, msg: DebugLinkDecision
) -> None: ) -> None:
from trezor import io, workflow from trezor import workflow
workflow.idle_timer.touch() workflow.idle_timer.touch()
@ -183,42 +164,21 @@ if __debug__:
x = msg.x # local_cache_attribute x = msg.x # local_cache_attribute
y = msg.y # local_cache_attribute y = msg.y # local_cache_attribute
event_id = debug_events.last_event + 1 # Incrementing the counter for last events so we know what to await
debug_events.last_event += 1 debug_events.last_event += 1
# TT click on specific coordinates, with possible hold # TT click on specific coordinates, with possible hold
if x is not None and y is not None and utils.MODEL in ("T",): if x is not None and y is not None and utils.MODEL in ("T",):
# Getting IDs and incrementing the counter for next events click_chan.publish((debug_events.last_event, x, y, msg.hold_ms))
# Sending two events - click down and click up # TR press specific button
first_evt_id = event_id elif msg.physical_button is not None and utils.MODEL in ("R",):
second_evt_id = debug_events.last_event + 1 button_chan.publish(
debug_events.last_event += 1 (debug_events.last_event, msg.physical_button, msg.hold_ms)
)
evt_down = (first_evt_id, io.TOUCH_START), x, y
evt_up = (second_evt_id, io.TOUCH_END), x, y
synthetic_event.publish(evt_down)
if msg.hold_ms is not None:
loop.schedule(touch_hold(second_evt_id, x, y, msg.hold_ms))
else:
synthetic_event.publish(evt_up)
# TR hold of a specific button
elif (
msg.physical_button is not None
and msg.hold_ms is not None
and utils.MODEL in ("R",)
):
if msg.physical_button == DebugPhysicalButton.LEFT_BTN:
btn = io.BUTTON_LEFT
elif msg.physical_button == DebugPhysicalButton.RIGHT_BTN:
btn = io.BUTTON_RIGHT
else:
raise wire.ProcessError("Unknown physical button")
synthetic_event.publish((io.BUTTON, (io.BUTTON_PRESSED, btn)))
loop.schedule(button_hold(btn, msg.hold_ms))
# Something more general # Something more general
else: else:
# Will get picked up by _dispatch_debuglink_decision eventually # Will get picked up by _dispatch_debuglink_decision eventually
debuglink_decision_chan.publish((event_id, msg)) debuglink_decision_chan.publish((debug_events.last_event, msg))
if msg.wait: if msg.wait:
# We wait for all the previously sent events # We wait for all the previously sent events
@ -251,8 +211,6 @@ if __debug__:
if msg.wait_word_pos: if msg.wait_word_pos:
m.reset_word_pos = await reset_word_index.take() m.reset_word_pos = await reset_word_index.take()
if msg.wait_word_list:
m.reset_word = " ".join(await reset_current_words.take())
return m return m
async def dispatch_DebugLinkRecordScreen( async def dispatch_DebugLinkRecordScreen(

@ -57,71 +57,95 @@ class RustLayout(ui.Layout):
from trezor.enums import DebugPhysicalButton from trezor.enums import DebugPhysicalButton
def create_tasks(self) -> tuple[loop.AwaitableTask, ...]: def create_tasks(self) -> tuple[loop.AwaitableTask, ...]:
from apps.debug import confirm_signal, input_signal
return ( return (
self.handle_input_and_rendering(), self.handle_input_and_rendering(),
self.handle_timers(), self.handle_timers(),
self.handle_swipe(), self.handle_swipe_signal(),
self.handle_button_click(), self.handle_button_signal(),
confirm_signal(), self.handle_result_signal(),
input_signal(),
) )
async def handle_result_signal(self) -> None:
"""Enables sending arbitrary input - ui.Result.
Waits for `result_signal` and carries it out.
"""
from apps.debug import result_signal
from storage import debug as debug_storage
while True:
event_id, result = await result_signal()
# Layout change will be notified in _first_paint of the next layout
debug_storage.new_layout_event_id = event_id
raise ui.Result(result)
def read_content(self) -> list[str]: def read_content(self) -> list[str]:
"""Gets the visible content of the screen.""" """Gets the visible content of the screen."""
self._place_layout() self._place_layout() # TODO: is this necessary?
raw = self._read_content_raw() raw = self._read_content_raw()
return " ".join(raw).split("\n") return " ".join(raw).split("\n")
def _press_left(self) -> Any: async def _press_left(self, hold_ms: int | None) -> Any:
"""Triggers left button press.""" """Triggers left button press."""
self.layout.button_event(io.BUTTON_PRESSED, io.BUTTON_LEFT) self.layout.button_event(io.BUTTON_PRESSED, io.BUTTON_LEFT)
self._paint() self._paint()
if hold_ms is not None:
await loop.sleep(hold_ms)
return self.layout.button_event(io.BUTTON_RELEASED, io.BUTTON_LEFT) return self.layout.button_event(io.BUTTON_RELEASED, io.BUTTON_LEFT)
def _press_right(self) -> Any: async def _press_right(self, hold_ms: int | None) -> Any:
"""Triggers right button press.""" """Triggers right button press."""
self.layout.button_event(io.BUTTON_PRESSED, io.BUTTON_RIGHT) self.layout.button_event(io.BUTTON_PRESSED, io.BUTTON_RIGHT)
self._paint() self._paint()
if hold_ms is not None:
await loop.sleep(hold_ms)
return self.layout.button_event(io.BUTTON_RELEASED, io.BUTTON_RIGHT) return self.layout.button_event(io.BUTTON_RELEASED, io.BUTTON_RIGHT)
def _press_middle(self) -> Any: async def _press_middle(self, hold_ms: int | None) -> Any:
"""Triggers middle button press.""" """Triggers middle button press."""
self.layout.button_event(io.BUTTON_PRESSED, io.BUTTON_LEFT) self.layout.button_event(io.BUTTON_PRESSED, io.BUTTON_LEFT)
self._paint() self._paint()
self.layout.button_event(io.BUTTON_PRESSED, io.BUTTON_RIGHT) self.layout.button_event(io.BUTTON_PRESSED, io.BUTTON_RIGHT)
self._paint() self._paint()
if hold_ms is not None:
await loop.sleep(hold_ms)
self.layout.button_event(io.BUTTON_RELEASED, io.BUTTON_LEFT) self.layout.button_event(io.BUTTON_RELEASED, io.BUTTON_LEFT)
self._paint() self._paint()
return self.layout.button_event(io.BUTTON_RELEASED, io.BUTTON_RIGHT) return self.layout.button_event(io.BUTTON_RELEASED, io.BUTTON_RIGHT)
def _press_button(self, btn_to_press: DebugPhysicalButton) -> Any: async def _press_button(
self,
event_id: int | None,
btn_to_press: DebugPhysicalButton,
hold_ms: int | None,
) -> Any:
from trezor.enums import DebugPhysicalButton from trezor.enums import DebugPhysicalButton
from trezor import workflow from trezor import workflow
from apps.debug import notify_layout_change from apps.debug import notify_layout_change
from storage import debug as debug_storage
if btn_to_press == DebugPhysicalButton.LEFT_BTN: if btn_to_press == DebugPhysicalButton.LEFT_BTN:
msg = self._press_left() msg = await self._press_left(hold_ms)
elif btn_to_press == DebugPhysicalButton.MIDDLE_BTN: elif btn_to_press == DebugPhysicalButton.MIDDLE_BTN:
msg = self._press_middle() msg = await self._press_middle(hold_ms)
elif btn_to_press == DebugPhysicalButton.RIGHT_BTN: elif btn_to_press == DebugPhysicalButton.RIGHT_BTN:
msg = self._press_right() msg = await self._press_right(hold_ms)
else: else:
raise Exception(f"Unknown button: {btn_to_press}") raise Exception(f"Unknown button: {btn_to_press}")
self.layout.paint()
if msg is not None: if msg is not None:
# Layout change will be notified in _first_paint of the next layout
debug_storage.new_layout_event_id = event_id
raise ui.Result(msg) raise ui.Result(msg)
# So that these presses will keep trezor awake # So that these presses will keep trezor awake
# (it will not be locked after auto_lock_delay_ms) # (it will not be locked after auto_lock_delay_ms)
workflow.idle_timer.touch() workflow.idle_timer.touch()
ui.refresh() # so that a screenshot is taken self._paint()
notify_layout_change(self) notify_layout_change(self, event_id)
def _swipe(self, direction: int) -> None: async def _swipe(self, event_id: int | None, direction: int) -> None:
"""Triggers swipe in the given direction. """Triggers swipe in the given direction.
Only `UP` and `DOWN` directions are supported. Only `UP` and `DOWN` directions are supported.
@ -135,9 +159,9 @@ class RustLayout(ui.Layout):
else: else:
raise Exception(f"Unsupported direction: {direction}") raise Exception(f"Unsupported direction: {direction}")
self._press_button(btn_to_press) await self._press_button(event_id, btn_to_press, None)
async def handle_swipe(self) -> None: async def handle_swipe_signal(self) -> None:
"""Enables pagination through the current page/flow page. """Enables pagination through the current page/flow page.
Waits for `swipe_signal` and carries it out. Waits for `swipe_signal` and carries it out.
@ -145,19 +169,19 @@ class RustLayout(ui.Layout):
from apps.debug import swipe_signal from apps.debug import swipe_signal
while True: while True:
direction = await swipe_signal() event_id, direction = await swipe_signal()
self._swipe(direction) await self._swipe(event_id, direction)
async def handle_button_click(self) -> None: async def handle_button_signal(self) -> None:
"""Enables clicking arbitrary of the three buttons. """Enables clicking arbitrary of the three buttons.
Waits for `model_r_btn_signal` and carries it out. Waits for `button_signal` and carries it out.
""" """
from apps.debug import model_r_btn_signal from apps.debug import button_signal
while True: while True:
btn = await model_r_btn_signal() event_id, btn, hold_ms = await button_signal()
self._press_button(btn) await self._press_button(event_id, btn, hold_ms)
else: else:
@ -172,12 +196,21 @@ class RustLayout(ui.Layout):
if __debug__ and self.should_notify_layout_change: if __debug__ and self.should_notify_layout_change:
from apps.debug import notify_layout_change from apps.debug import notify_layout_change
from storage import debug as debug_storage
# notify about change and do not notify again until next await. # notify about change and do not notify again until next await.
# (handle_rendering might be called multiple times in a single await, # (handle_rendering might be called multiple times in a single await,
# because of the endless loop in __iter__) # because of the endless loop in __iter__)
self.should_notify_layout_change = False self.should_notify_layout_change = False
notify_layout_change(self)
# Possibly there is an event ID that caused the layout change,
# so notifying with this ID.
event_id = None
if debug_storage.new_layout_event_id is not None:
event_id = debug_storage.new_layout_event_id
debug_storage.new_layout_event_id = None
notify_layout_change(self, event_id)
# Turn the brightness on again. # Turn the brightness on again.
ui.backlight_fade(self.BACKLIGHT_LEVEL) ui.backlight_fade(self.BACKLIGHT_LEVEL)

@ -31,7 +31,11 @@ class HomescreenBase(RustLayout):
# In __debug__ mode, ignore {confirm,swipe,input}_signal. # In __debug__ mode, ignore {confirm,swipe,input}_signal.
def create_tasks(self) -> tuple[loop.AwaitableTask, ...]: def create_tasks(self) -> tuple[loop.AwaitableTask, ...]:
return self.handle_timers(), self.handle_input_and_rendering() return (
self.handle_timers(),
self.handle_input_and_rendering(),
self.handle_button_signal(), # so we can receive debug events
)
class Homescreen(HomescreenBase): class Homescreen(HomescreenBase):

@ -56,16 +56,27 @@ class RustLayout(ui.Layout):
if __debug__: if __debug__:
def create_tasks(self) -> tuple[loop.AwaitableTask, ...]: def create_tasks(self) -> tuple[loop.AwaitableTask, ...]:
from apps.debug import confirm_signal, input_signal
return ( return (
self.handle_timers(), self.handle_timers(),
self.handle_input_and_rendering(), self.handle_input_and_rendering(),
self.handle_swipe(), self.handle_swipe(),
confirm_signal(), self.handle_click_signal(),
input_signal(), self.handle_result_signal(),
) )
async def handle_result_signal(self) -> None:
"""Enables sending arbitrary input - ui.Result.
Waits for `result_signal` and carries it out.
"""
from apps.debug import result_signal
from storage import debug as debug_storage
while True:
event_id, result = await result_signal()
debug_storage.new_layout_event_id = event_id
raise ui.Result(result)
def read_content(self) -> list[str]: def read_content(self) -> list[str]:
result: list[str] = [] result: list[str] = []
@ -103,6 +114,45 @@ class RustLayout(ui.Layout):
notify_layout_change(self, event_id) notify_layout_change(self, event_id)
async def _click(
self,
event_id: int | None,
x: int,
y: int,
hold_ms: int | None,
) -> Any:
from trezor import workflow
from apps.debug import notify_layout_change
from storage import debug as debug_storage
self.layout.touch_event(io.TOUCH_START, x, y)
self._paint()
if hold_ms is not None:
await loop.sleep(hold_ms)
msg = self.layout.touch_event(io.TOUCH_END, x, y)
if msg is not None:
debug_storage.new_layout_event_id = event_id
raise ui.Result(msg)
# So that these presses will keep trezor awake
# (it will not be locked after auto_lock_delay_ms)
workflow.idle_timer.touch()
self._paint()
notify_layout_change(self, event_id)
async def handle_click_signal(self) -> None:
"""Enables clicking somewhere on the screen.
Waits for `click_signal` and carries it out.
"""
from apps.debug import click_signal
while True:
event_id, x, y, hold_ms = await click_signal()
await self._click(event_id, x, y, hold_ms)
else: else:
def create_tasks(self) -> tuple[loop.AwaitableTask, ...]: def create_tasks(self) -> tuple[loop.AwaitableTask, ...]:
@ -138,35 +188,16 @@ class RustLayout(ui.Layout):
def handle_input_and_rendering(self) -> loop.Task: # type: ignore [awaitable-is-generator] def handle_input_and_rendering(self) -> loop.Task: # type: ignore [awaitable-is-generator]
from trezor import workflow from trezor import workflow
if __debug__:
from apps.debug import notify_layout_change, synthetic_event_signal
from storage import debug as debug_storage
touch = loop.wait(io.TOUCH) touch = loop.wait(io.TOUCH)
self._first_paint() self._first_paint()
while True: while True:
# Using `yield` instead of `await` to avoid allocations. # Using `yield` instead of `await` to avoid allocations.
event_id: int | None = None event, x, y = yield touch
if __debug__:
# When using `yield` here, it misses some events
event, x, y = await loop.race(touch, synthetic_event_signal())
if isinstance(event, tuple):
event_id, event = event
else:
event, x, y = yield touch
workflow.idle_timer.touch() workflow.idle_timer.touch()
msg = None msg = None
if event in (io.TOUCH_START, io.TOUCH_MOVE, io.TOUCH_END): if event in (io.TOUCH_START, io.TOUCH_MOVE, io.TOUCH_END):
msg = self.layout.touch_event(event, x, y) msg = self.layout.touch_event(event, x, y)
if __debug__:
if msg is not None:
# Going to new layout - notify about change there (in first paint)
debug_storage.new_layout_event_id = event_id
else:
# Layout change happens in this layout
notify_layout_change(self, event_id)
if msg is not None: if msg is not None:
raise ui.Result(msg) raise ui.Result(msg)
self._paint() self._paint()

@ -26,15 +26,11 @@ if __debug__:
) )
async def handle_debug_confirm(self) -> None: async def handle_debug_confirm(self) -> None:
from apps.debug import confirm_signal from apps.debug import result_signal
try: _event_id, result = await result_signal()
await confirm_signal() if result is not trezorui2.CONFIRMED:
except Result as r: raise Result(result)
if r.value is not trezorui2.CONFIRMED:
raise
else:
return
for event, x, y in ( for event, x, y in (
(io.TOUCH_START, 220, 220), (io.TOUCH_START, 220, 220),

@ -31,7 +31,11 @@ class HomescreenBase(RustLayout):
# In __debug__ mode, ignore {confirm,swipe,input}_signal. # In __debug__ mode, ignore {confirm,swipe,input}_signal.
def create_tasks(self) -> tuple[loop.AwaitableTask, ...]: def create_tasks(self) -> tuple[loop.AwaitableTask, ...]:
return self.handle_timers(), self.handle_input_and_rendering() return (
self.handle_timers(),
self.handle_input_and_rendering(),
self.handle_click_signal(), # so we can receive debug events
)
class Homescreen(HomescreenBase): class Homescreen(HomescreenBase):

@ -668,16 +668,13 @@ class DebugLink:
) )
def press_right_htc( def press_right_htc(
self, hold_ms: int, extra_ms: int = 200 self, hold_ms: int, extra_ms: int = 200, wait: bool = True
) -> Optional[LayoutContent]: ) -> Optional[LayoutContent]:
hold_ms = hold_ms + extra_ms # safety margin return self.input(
result = self.input(
physical_button=messages.DebugPhysicalButton.RIGHT_BTN, physical_button=messages.DebugPhysicalButton.RIGHT_BTN,
hold_ms=hold_ms, hold_ms=hold_ms + extra_ms, # safety margin
wait=wait,
) )
# sleeping little longer for UI to update
time.sleep(hold_ms / 1000 + 0.1)
return result
def stop(self) -> None: def stop(self) -> None:
self._call(messages.DebugLinkStop(), nowait=True) self._call(messages.DebugLinkStop(), nowait=True)

@ -47,6 +47,8 @@ def go_next(debug: "DebugLink", wait: bool = False) -> "LayoutContent" | None:
return debug.click(buttons.OK, wait=wait) # type: ignore return debug.click(buttons.OK, wait=wait) # type: ignore
elif debug.model == "R": elif debug.model == "R":
return debug.press_right(wait=wait) # type: ignore return debug.press_right(wait=wait) # type: ignore
else:
raise ValueError(f"Unknown model {debug.model}")
def go_back(debug: "DebugLink", wait: bool = False) -> "LayoutContent" | None: def go_back(debug: "DebugLink", wait: bool = False) -> "LayoutContent" | None:
@ -54,6 +56,8 @@ def go_back(debug: "DebugLink", wait: bool = False) -> "LayoutContent" | None:
return debug.click(buttons.CANCEL, wait=wait) # type: ignore return debug.click(buttons.CANCEL, wait=wait) # type: ignore
elif debug.model == "R": elif debug.model == "R":
return debug.press_left(wait=wait) # type: ignore return debug.press_left(wait=wait) # type: ignore
else:
raise ValueError(f"Unknown model {debug.model}")
def navigate_to_action_and_press( def navigate_to_action_and_press(

@ -112,13 +112,15 @@ def read_words(
words.extend(layout.seed_words()) words.extend(layout.seed_words())
# There is hold-to-confirm button # There is hold-to-confirm button
if debug.model == "T": if do_htc:
if debug.model == "T":
debug.click(buttons.OK, hold_ms=1500, wait=True)
elif debug.model == "R":
debug.press_right_htc(1000)
else:
# It would take a very long time to test 16-of-16 with doing 1500 ms HTC after # It would take a very long time to test 16-of-16 with doing 1500 ms HTC after
# each word set # each word set
if do_htc: debug.press_yes()
debug.click(buttons.OK, hold_ms=1500, wait=True)
else:
debug.press_yes()
return words return words

@ -73,11 +73,10 @@ def set_autolock_delay(device_handler: "BackgroundDeviceHandler", delay_ms: int)
) )
if debug.model == "T": if debug.model == "T":
debug.click(buttons.OK) layout = debug.click(buttons.OK, wait=True)
elif debug.model == "R": elif debug.model == "R":
debug.press_right() layout = debug.press_right(wait=True)
layout = debug.wait_layout()
assert "Homescreen" in layout.str_content assert "Homescreen" in layout.str_content
assert device_handler.result() == "Settings applied" assert device_handler.result() == "Settings applied"
@ -301,12 +300,12 @@ def test_dryrun_locks_at_number_of_words(device_handler: "BackgroundDeviceHandle
if debug.model == "T": if debug.model == "T":
# Need to click two times to get the correct layout # Need to click two times to get the correct layout
# because of the lockscreen # because of the lockscreen
layout = debug.click(buttons.OK, wait=True) debug.click(buttons.OK, wait=True)
layout = debug.click(buttons.OK, wait=True) layout = debug.click(buttons.OK, wait=True)
assert "PinKeyboard" in layout.str_content assert "PinKeyboard" in layout.str_content
elif debug.model == "R": elif debug.model == "R":
# Doing a short HTC to simulate a real click # Again needs two waits to get the correct layout
debug.press_right_htc(hold_ms=100) debug.press_right(wait=True)
layout = debug.wait_layout() layout = debug.wait_layout()
assert "PinEntry" in layout.str_content assert "PinEntry" in layout.str_content
layout = debug.input(PIN4, wait=True) layout = debug.input(PIN4, wait=True)

@ -14,7 +14,6 @@
# You should have received a copy of the License along with this library. # 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>. # If not, see <https://www.gnu.org/licenses/lgpl-3.0.html>.
import time
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
import pytest import pytest
@ -37,10 +36,9 @@ def test_hold_to_lock(device_handler: "BackgroundDeviceHandler"):
def hold(duration: int, wait: bool = True) -> None: def hold(duration: int, wait: bool = True) -> None:
if debug.model == "R": if debug.model == "R":
debug.press_right_htc(hold_ms=duration) debug.press_right_htc(hold_ms=duration, wait=wait)
else: else:
debug.input(x=13, y=37, hold_ms=duration, wait=wait) debug.input(x=13, y=37, hold_ms=duration, wait=wait)
time.sleep(duration / 1000 + 0.5)
assert device_handler.features().unlocked is False assert device_handler.features().unlocked is False

@ -119,7 +119,9 @@ def _slip39_advanced_reset(
for _ in range(group_count): for _ in range(group_count):
for _ in range(share_count): for _ in range(share_count):
# read words # read words
words = reset.read_words(debug, messages.BackupType.Slip39_Advanced, do_htc=False) words = reset.read_words(
debug, messages.BackupType.Slip39_Advanced, do_htc=False
)
# confirm words # confirm words
reset.confirm_words(debug, words) reset.confirm_words(debug, words)

@ -36,6 +36,7 @@ def prepare_tutorial_and_cancel_after_it(
) -> Generator["DebugLink", None, None]: ) -> Generator["DebugLink", None, None]:
debug = device_handler.debuglink() debug = device_handler.debuglink()
device_handler.run(device.show_device_tutorial) device_handler.run(device.show_device_tutorial)
debug.wait_layout()
yield debug yield debug
@ -45,7 +46,7 @@ def prepare_tutorial_and_cancel_after_it(
def go_through_tutorial(debug: "DebugLink") -> None: def go_through_tutorial(debug: "DebugLink") -> None:
debug.press_right(wait=True) debug.press_right(wait=True)
debug.press_right(wait=True) debug.press_right(wait=True)
debug.press_right_htc(hold_ms=1000) debug.press_right_htc(hold_ms=1000, wait=True)
debug.press_right(wait=True) debug.press_right(wait=True)
debug.press_right(wait=True) debug.press_right(wait=True)
layout = debug.press_middle(wait=True) layout = debug.press_middle(wait=True)

@ -200,6 +200,9 @@ def client(
request.session.shouldstop = "Failed to communicate with Trezor" request.session.shouldstop = "Failed to communicate with Trezor"
pytest.fail("Failed to communicate with Trezor") pytest.fail("Failed to communicate with Trezor")
# Resetting all the debug events with this
_raw_client.debug.watch_layout(False)
if test_ui: if test_ui:
# we need to reseed before the wipe # we need to reseed before the wipe
_raw_client.debug.reseed(0) _raw_client.debug.reseed(0)

@ -1176,7 +1176,6 @@ class InputFlowBip39RecoveryDryRun(InputFlowBase):
assert "WORD" in self.layout().title() assert "WORD" in self.layout().title()
self.debug.input(word) self.debug.input(word)
self.debug.wait_layout()
self.debug.press_right() self.debug.press_right()
yield yield
self.debug.press_yes() self.debug.press_yes()

@ -697,45 +697,45 @@
}, },
"TR": { "TR": {
"click_tests": { "click_tests": {
"TR_test_autolock.py::test_autolock_does_not_interrupt_preauthorized": "a4479d59a39611bf43210095f53a278f0dac5be3dad86ed476ecd3430741d583", "TR_test_autolock.py::test_autolock_does_not_interrupt_preauthorized": "af6935488ae01501e9e83ed6c729879f13fd3d84915488db98d9b2ad79b1b9ff",
"TR_test_autolock.py::test_autolock_does_not_interrupt_signing": "7e0a7fccbae1cf66671ca728e791e9757a363c6ae8960c2346d8c6aca666c4b2", "TR_test_autolock.py::test_autolock_does_not_interrupt_signing": "61e5cb6eaf2fc1d794991f0689d61e8ddd2e36e950a5d92de2600703d8270b58",
"TR_test_autolock.py::test_autolock_interrupts_passphrase": "0faa1c1a538d3b2c58bbe2b22b979125c51ba7473deec4078b8a152562c8a977", "TR_test_autolock.py::test_autolock_interrupts_passphrase": "04d35a1d4878daa58c91bda51b28e135fe1a2864f3b7eb2cc199b5800345e396",
"TR_test_autolock.py::test_autolock_interrupts_signing": "c95681f193c90df6aa57b37bea0049a4173dc657158c60ed22a733121ba8b859", "TR_test_autolock.py::test_autolock_interrupts_signing": "188aefa3cb768e2ef94590aacd8cc6a961175004b3919547dab1fb67cdd09901",
"TR_test_autolock.py::test_autolock_passphrase_keyboard": "0c91241f889fb6b0950420d878a8b2f85a67795235c7dc1973fb44c221c9318a", "TR_test_autolock.py::test_autolock_passphrase_keyboard": "77c0efface8caaedad05ea8c5900d29a7002c6c6f57ba5b0e0be1524f540ab08",
"TR_test_autolock.py::test_dryrun_enter_word_slowly": "4cd93dd877ba173376b6e9f36bda3805c89ccdd86b5aa08fcee856008678a2d4", "TR_test_autolock.py::test_dryrun_enter_word_slowly": "b4a5526e9d761a69b27875d6cb03001912b910ecd148cb8b776a4d5b092c12d2",
"TR_test_autolock.py::test_dryrun_locks_at_number_of_words": "254f203f761d297bd996aaa92303578a05ed4dca5a450f91c6cbe61d78123952", "TR_test_autolock.py::test_dryrun_locks_at_number_of_words": "990d72f473d8efd2f299e74af78692c7aef3c60c44638cd689dffa9ce589b5b3",
"TR_test_autolock.py::test_dryrun_locks_at_word_entry": "f822aa0cbbc05b31bdc4db4d3183eae4b822e8bf3184f3d97c38ed98705edada", "TR_test_autolock.py::test_dryrun_locks_at_word_entry": "328b70515909b5afbc919592358dd7ee5421cc8e504d4b0ea79f42934e9c5559",
"TR_test_lock.py::test_hold_to_lock": "7038f39afc7aa10640d91ada92656389b4989d885d49a43356976ac142b6aa9a", "TR_test_lock.py::test_hold_to_lock": "7038f39afc7aa10640d91ada92656389b4989d885d49a43356976ac142b6aa9a",
"TR_test_passphrase_tr.py::test_cancel": "32e55986b0bcda321feb908e771909da59c28871d755fdbf9960744403e21c0b", "TR_test_passphrase_tr.py::test_cancel": "5e79367294b6ae6445289906428097af26e4a2d1797df7095cfcf1c7d658046d",
"TR_test_passphrase_tr.py::test_passphrase_delete": "183f40a7b2c40f1303ad4d6d08312ec04f38fd2a81ba3de6b55d671593d6fef5", "TR_test_passphrase_tr.py::test_passphrase_delete": "789a89c318b8d6ebf561d0730081946182d464658294ffb6784bcf78724fb3d6",
"TR_test_passphrase_tr.py::test_passphrase_input[Y@14lw%p)JN@f54MYvys@zj'g-mnkoxeaMzLgfCxUdDSZW-381132c0": "ae08209522ff09ea34c9ec91ef8b38168fa0df8381baaeb05046952233392f22", "TR_test_passphrase_tr.py::test_passphrase_input[Y@14lw%p)JN@f54MYvys@zj'g-mnkoxeaMzLgfCxUdDSZW-381132c0": "7caeb5e0ab810b72a88abd492cdb90c7a3ba4c783652e766f561c77b7bf5ec21",
"TR_test_passphrase_tr.py::test_passphrase_input[aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-166c898a": "8c65664112a3486d40305d0006357041c77aed9c042f27461c2320987c768860", "TR_test_passphrase_tr.py::test_passphrase_input[aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-166c898a": "6349515fe41dfd99362c239e1e6171bccb86651483d89f5d6c86cba531017ea7",
"TR_test_passphrase_tr.py::test_passphrase_input[aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-334262e9": "582d0fdf15f4a1f021e45153451d273728090a805e6433547471ceb977b4e84e", "TR_test_passphrase_tr.py::test_passphrase_input[aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-334262e9": "5adba8f36d96a98248221d44d8a8b899ddada3d8107d33595108778d17f96e7c",
"TR_test_passphrase_tr.py::test_passphrase_input[abc 123-mvqzZUb9NaUc62Buk9WCP4L7hunsXFyamT]": "51f75222d1668738b32bbbd1f0b99ff6a7ee4c494aa4e3fc551600cdfcea1a27", "TR_test_passphrase_tr.py::test_passphrase_input[abc 123-mvqzZUb9NaUc62Buk9WCP4L7hunsXFyamT]": "5dc1c896ce689911f2d44a9c3d65655e2ab55cbc68c143bb181b5b5ba2e76cfc",
"TR_test_passphrase_tr.py::test_passphrase_input[abc123ABC_<>-mtHHfh6uHtJiACwp7kzJZ97yueT6sEdQiG]": "e757dd50a11d98ab4cef52059c2e6e6ca50d35b94112b2e2284936d1ed9aa828", "TR_test_passphrase_tr.py::test_passphrase_input[abc123ABC_<>-mtHHfh6uHtJiACwp7kzJZ97yueT6sEdQiG]": "c92e48e2dee2c2cbaead6077d6e5dacaf300f224709a87224f1881dfc2657905",
"TR_test_passphrase_tr.py::test_passphrase_input_over_50_chars": "ae55ccc679bbab7efe3ae546c683b91de1032c2eefe1845b4e6543f7725ec5d3", "TR_test_passphrase_tr.py::test_passphrase_input_over_50_chars": "10e52c457d523afd387104609d9a35cbf40df5d3bb7cbd0c9d7236a60e4e1fc6",
"TR_test_passphrase_tr.py::test_passphrase_loop_all_characters": "b5cb25d78372c9ad55e78472369f401bce96c9e637a2e14dc23c41d3e9ed2bac", "TR_test_passphrase_tr.py::test_passphrase_loop_all_characters": "4a7e3b18e6214a4c6962145fd3f97e0cb0c2547d838d56805d1c20d91f2e26e3",
"TR_test_pin.py::test_pin_change": "6c5cfbcd4631f21eb781556528b8ef704a257f2e77a46b0538bfd99cfcbc73e6", "TR_test_pin.py::test_pin_change": "bac906a59d56900f90a125843360ba2e1984d37721955769ffd952b4996bf993",
"TR_test_pin.py::test_pin_incorrect": "2c6e81bc3331525af99841c5dd79e738ed2a35439592f2a9e1a16591496db7ba", "TR_test_pin.py::test_pin_incorrect": "ebb48ada5b0430ca562462ddc3c855cdbf133efabaa5e6318f058f2c2f914f23",
"TR_test_pin.py::test_pin_long": "ba2b5442d3c4b12c3a63f785920576f67ed86f95ae3967a960c7626d6e2e9b15", "TR_test_pin.py::test_pin_long": "5ea37cdbe11f6773ea1aafb7fa81b16fda31ac810af0e5dad7dc35b12c1e5645",
"TR_test_pin.py::test_pin_long_delete": "840fd586b2eca792f7ac5b3ae472371d4d0db3184e9a6303871620dff745eff5", "TR_test_pin.py::test_pin_long_delete": "d329ebad67773c012fc7ef7ff18b1e720e6b31b0b3afc91cd7501adcfc05f178",
"TR_test_pin.py::test_pin_longer_than_max": "3c13175230f7ed3d12e26e0bbca0377a5ed090e5ef8f31215132c385f4fd78e2", "TR_test_pin.py::test_pin_longer_than_max": "b93d02197bad78b4cdf4fe2766604607834ffc3f6d1981aecfc67d896117bd72",
"TR_test_pin.py::test_pin_same_as_wipe_code": "7a8d370d21ecc71058ee15b6d1c69d0ef8540c30185b408b7584eb784917cbcc", "TR_test_pin.py::test_pin_same_as_wipe_code": "801f58ca38219ce38f6804697baef94f0de1258d9fcb4f51ebc3d7498467bd2f",
"TR_test_pin.py::test_pin_setup": "e5ac93f7e14b493c7dae04f69f671f7c8159208df4b82efe94c51b81ca3696ea", "TR_test_pin.py::test_pin_setup": "2d4389f9a63a57e6022928a5ed93b40c71ceb0a64f0d963198d0b0287dbed0c8",
"TR_test_pin.py::test_pin_setup_mismatch": "f9a37629e03350250aadeeb5a1d41628cbda60a54b863bfcf71204c7e28a5a2e", "TR_test_pin.py::test_pin_setup_mismatch": "b03768bf0b9f6675539646865296dd195be4d09c9b3d114aabbb111a641d931d",
"TR_test_pin.py::test_pin_short": "ea33035f923447bc3afdc6d3ca5b4d5e5696ac4102c7d896192857f3a823ae1a", "TR_test_pin.py::test_pin_short": "1ed7da1bc77ae245e0d4c33791dd826505bb98ce556e10faefdc1dc76b077360",
"TR_test_pin.py::test_wipe_code_same_as_pin": "d2883c4ebb7b8c09593e4a1ad962132fb261abdefb7abe157a9c2cb1c875b43e", "TR_test_pin.py::test_wipe_code_same_as_pin": "e9e7e049fa5b2c05b9cbd83f201a522420b05c0e2219286f9b3426e404efe09e",
"TR_test_pin.py::test_wipe_code_setup": "03db18acb089e0c7438c9b9e79e17bf8aa3a9dd8467f91279d9323783e1f10e9", "TR_test_pin.py::test_wipe_code_setup": "a8993cdb355fe713933356341cc363d0703261e209aced6c80b591e5ed5f5d2c",
"TR_test_recovery.py::test_recovery_bip39": "48fe32b05771080a4dfaacddeb63b322c7269115f0cdbcf3ab3610fa2f64580e", "TR_test_recovery.py::test_recovery_bip39": "7d3ea332623b84b309fc8749dc21ed49f09c3114753834fc08f9dcdf6d0c0ecf",
"TR_test_recovery.py::test_recovery_slip39_basic": "244c609f395a4e88789f857cb51f5577b145c912f357717d667bca62e20d6bad", "TR_test_recovery.py::test_recovery_slip39_basic": "3f4effc83ae39f45fe683272a72e37399353fc4139fa807f9273572593d89b3b",
"TR_test_reset_bip39.py::test_reset_bip39": "27181b86545df487343375d05806c191062809ae29aa5298d191f76a5a2fb48c", "TR_test_reset_bip39.py::test_reset_bip39": "196400e33cfe2ec0f0c1dad4f37e58c60866f4e8f6506866fdede28dd0b29c90",
"TR_test_reset_slip39_advanced.py::test_reset_slip39_advanced_16of16groups_16of16shares": "b6d32e0206f793e0b4fd91dde49f5831f83502f655b20912e9d3c7e1fad18275", "TR_test_reset_slip39_advanced.py::test_reset_slip39_advanced_16of16groups_16of16shares": "9d17020acf413873fb338697e074c9bf03dcf97d60dbc30169c3fcc21ae93c9e",
"TR_test_reset_slip39_advanced.py::test_reset_slip39_advanced_2of2groups_2of2shares": "9cfeeda665bbee154f18bb774ff28f87bb0577e46dc38ae23d8a9b4b9a5e6255", "TR_test_reset_slip39_advanced.py::test_reset_slip39_advanced_2of2groups_2of2shares": "7153250d3d9539eaee44f3bf60aa675f890cdbd230420e2a05126d77e783d184",
"TR_test_reset_slip39_basic.py::test_reset_slip39_basic_16of16": "3283e383f930c31ace9ac837f1cf05f7d67c34c29e0cc9a275f2dba7840f6cdc", "TR_test_reset_slip39_basic.py::test_reset_slip39_basic_16of16": "4a4f1a507a28d1b66e36154db7d56628034e9061a140e766175dbf7dbd11029e",
"TR_test_reset_slip39_basic.py::test_reset_slip39_basic_1of1": "7ecd7789a0dcb8e5dd07213040f89bf89e5942894dccd6d1544968b6a92017dc", "TR_test_reset_slip39_basic.py::test_reset_slip39_basic_1of1": "8242865f813cf1a3a816661e6d94614b6d1e69c59eb6b5c17c16925e69002992",
"TR_test_tutorial.py::test_tutorial_again_and_skip": "756ec36eeccadc8040677f654a9188c6d5b7bbdc784b9d08d662508b61a013ac", "TR_test_tutorial.py::test_tutorial_again_and_skip": "db80b891aaf5266e98962527993e4e335399d7356746fe63ca0879451462c091",
"TR_test_tutorial.py::test_tutorial_finish": "136588edb98e21272c68661c467e1c41bacc0a9b475d15f59bfcf22f6a3b9d08", "TR_test_tutorial.py::test_tutorial_finish": "6820baed02fc5967c8b4b1634be24de934db92c26cd13b391d2732a9526d7635",
"TR_test_tutorial.py::test_tutorial_skip": "2d461f1cc9848d51435d5ccaf89a60db870e3f0a353212e99a8b098c90dabac0" "TR_test_tutorial.py::test_tutorial_skip": "6beeace983acf29f6f77cd69e911c86592c9a45eb8a318d09c729a1a71ed97cb"
}, },
"device_tests": { "device_tests": {
"TR_binance-test_get_address.py::test_binance_get_address[m-44h-714h-0h-0-0-bnb1hgm0p7khfk85zpz-68e2cb5a": "d1d72604826a36dc2ead1b56f770868207c9929533236609ccefa8a8fdadd60a", "TR_binance-test_get_address.py::test_binance_get_address[m-44h-714h-0h-0-0-bnb1hgm0p7khfk85zpz-68e2cb5a": "d1d72604826a36dc2ead1b56f770868207c9929533236609ccefa8a8fdadd60a",
@ -1659,9 +1659,9 @@
"TR_reset_recovery-test_recovery_bip39_dryrun.py::test_bad_parameters[passphrase_protection-True]": "e9533f210cda5d7b96fbd84c17ae12cf83218b2b68e2185f49e7033a69d6792d", "TR_reset_recovery-test_recovery_bip39_dryrun.py::test_bad_parameters[passphrase_protection-True]": "e9533f210cda5d7b96fbd84c17ae12cf83218b2b68e2185f49e7033a69d6792d",
"TR_reset_recovery-test_recovery_bip39_dryrun.py::test_bad_parameters[pin_protection-True]": "e9533f210cda5d7b96fbd84c17ae12cf83218b2b68e2185f49e7033a69d6792d", "TR_reset_recovery-test_recovery_bip39_dryrun.py::test_bad_parameters[pin_protection-True]": "e9533f210cda5d7b96fbd84c17ae12cf83218b2b68e2185f49e7033a69d6792d",
"TR_reset_recovery-test_recovery_bip39_dryrun.py::test_bad_parameters[u2f_counter-1]": "e9533f210cda5d7b96fbd84c17ae12cf83218b2b68e2185f49e7033a69d6792d", "TR_reset_recovery-test_recovery_bip39_dryrun.py::test_bad_parameters[u2f_counter-1]": "e9533f210cda5d7b96fbd84c17ae12cf83218b2b68e2185f49e7033a69d6792d",
"TR_reset_recovery-test_recovery_bip39_dryrun.py::test_dry_run": "8648f3559d1810c07937a285d750fe17996d673a9ad3a3163506b90585bc2818", "TR_reset_recovery-test_recovery_bip39_dryrun.py::test_dry_run": "8ace2543ac2c5190e5c4c03410606c4708f6ab84c15460217bef4940e19ed7dc",
"TR_reset_recovery-test_recovery_bip39_dryrun.py::test_invalid_seed_core": "43241e8c57808a7243ca948a8afa4b2d9a9d2a1a9dafea272ed7cb404d888d2f", "TR_reset_recovery-test_recovery_bip39_dryrun.py::test_invalid_seed_core": "360d49527425f0b286691db83b913a2b8e945415a3ac0257f5db2daec094d714",
"TR_reset_recovery-test_recovery_bip39_dryrun.py::test_seed_mismatch": "aa431ba0e34a17dda6b4133ad5289e484a41cc18efdbd26cc2ffc680493beb48", "TR_reset_recovery-test_recovery_bip39_dryrun.py::test_seed_mismatch": "10736aecc57f44effad5e6ca7ecfaa4999418c4810184ff14b844210b6b51744",
"TR_reset_recovery-test_recovery_bip39_dryrun.py::test_uninitialized": "c86cb7f895fb74064cb765c269611d667f76e745a09ad56aaae2ba05b9683909", "TR_reset_recovery-test_recovery_bip39_dryrun.py::test_uninitialized": "c86cb7f895fb74064cb765c269611d667f76e745a09ad56aaae2ba05b9683909",
"TR_reset_recovery-test_recovery_bip39_t2.py::test_already_initialized": "e9533f210cda5d7b96fbd84c17ae12cf83218b2b68e2185f49e7033a69d6792d", "TR_reset_recovery-test_recovery_bip39_t2.py::test_already_initialized": "e9533f210cda5d7b96fbd84c17ae12cf83218b2b68e2185f49e7033a69d6792d",
"TR_reset_recovery-test_recovery_bip39_t2.py::test_tt_nopin_nopassphrase": "28c8478ae971b4c7c8c84b7504f4c768662463b733f7f30dc3760f345c892007", "TR_reset_recovery-test_recovery_bip39_t2.py::test_tt_nopin_nopassphrase": "28c8478ae971b4c7c8c84b7504f4c768662463b733f7f30dc3760f345c892007",
@ -1886,47 +1886,47 @@
}, },
"TT": { "TT": {
"click_tests": { "click_tests": {
"TT_test_autolock.py::test_autolock_does_not_interrupt_preauthorized": "03e156da82e7acd3509f831d1fc2e635fcebc673f3c14d61d141273e608fc7a4", "TT_test_autolock.py::test_autolock_does_not_interrupt_preauthorized": "e04f6bd04b26e4c1b669943701eb6f81ead01aff04008c30f75c4872dd711bc0",
"TT_test_autolock.py::test_autolock_does_not_interrupt_signing": "6711f75420f88f7500a6adfa8d9d25007871f698e08f048a80450085d9452133", "TT_test_autolock.py::test_autolock_does_not_interrupt_signing": "ad03151b8418ecf7277a78a35938c9ea169c99f8a0c5ab4a8ae54377ad97ca70",
"TT_test_autolock.py::test_autolock_interrupts_passphrase": "d1f797544d62708739c340dd4aa4ee28cfc65e9b643f595a55706de5917ee82f", "TT_test_autolock.py::test_autolock_interrupts_passphrase": "9c7844133c0ce5c841f5546a15361b1d021f754c45deb92abba649910f858067",
"TT_test_autolock.py::test_autolock_interrupts_signing": "ae4d3cc00fd5482b4499eff1c73aec75f4153e34950db910d4f787cc5f9b4208", "TT_test_autolock.py::test_autolock_interrupts_signing": "ad03151b8418ecf7277a78a35938c9ea169c99f8a0c5ab4a8ae54377ad97ca70",
"TT_test_autolock.py::test_autolock_passphrase_keyboard": "d1be209e13144850fc8b2cca8fded027cd53d2a9990af3c5be1db489995dc821", "TT_test_autolock.py::test_autolock_passphrase_keyboard": "9c7844133c0ce5c841f5546a15361b1d021f754c45deb92abba649910f858067",
"TT_test_autolock.py::test_dryrun_enter_word_slowly": "d3f2dc99db5a3fea22c075a74f15e8cf6a1ca655be29042eb845f033851edf78", "TT_test_autolock.py::test_dryrun_enter_word_slowly": "74ad36b3c4f6c2b1589d2cb3c7059790718218a70e85cded713899942bd07933",
"TT_test_autolock.py::test_dryrun_locks_at_number_of_words": "b9a320b598b73d52a4d9e117e3c7c962ccaccb8a386f52a836add95be0f897b6", "TT_test_autolock.py::test_dryrun_locks_at_number_of_words": "74ad36b3c4f6c2b1589d2cb3c7059790718218a70e85cded713899942bd07933",
"TT_test_autolock.py::test_dryrun_locks_at_word_entry": "092e18ed76d31d6b608a6a4b8ca2d742d070903c2c0090ef3760d2b9ae70f238", "TT_test_autolock.py::test_dryrun_locks_at_word_entry": "74ad36b3c4f6c2b1589d2cb3c7059790718218a70e85cded713899942bd07933",
"TT_test_lock.py::test_hold_to_lock": "73bfeda4595c74c9c08b552651920beb903b59bb6f1b08fcb29399f92a6a6ade", "TT_test_lock.py::test_hold_to_lock": "929ad4987e920f4cf6a0bf9689c31dee0509109c3c32835cdc22bd97841fdbe1",
"TT_test_passphrase_tt.py::test_passphrase_click_same_button_many_times": "1e8778a6aece69bdaa3fc5678e74bd282f7633f95c4f34e288472086950233c7", "TT_test_passphrase_tt.py::test_passphrase_click_same_button_many_times": "67e72c423dc025569b51c8870c1308abcf090cb87c8e98631ea54f4dbc406f6f",
"TT_test_passphrase_tt.py::test_passphrase_delete": "14d66f44f33520849c082f1f26a1559cbc57959e782dd2357e6e5759f0030957", "TT_test_passphrase_tt.py::test_passphrase_delete": "b92f9ec5db4a4c67599ff2b899ce2394dac4a095da30aa8bad7c2c15a0a014f9",
"TT_test_passphrase_tt.py::test_passphrase_delete_all": "684b4ce76c5fb620a2409c81e4c760620858564515cddf0ba380a5fc4525eca1", "TT_test_passphrase_tt.py::test_passphrase_delete_all": "2fde55a5bbc98ebf732c70557a2b3d79af9116f413c62a32d6c4e0281b0a4d44",
"TT_test_passphrase_tt.py::test_passphrase_dollar_sign_deletion": "9805f7a79fda34020e5f0ea4ab8f30f1e841d696702e7f1dd88241b8f2819345", "TT_test_passphrase_tt.py::test_passphrase_dollar_sign_deletion": "c6b96c572d841d4fe845c2e5dd5902d298678a0c4b8da588678cec35245681ac",
"TT_test_passphrase_tt.py::test_passphrase_input[Y@14lw%p)JN@f54MYvys@zj'g-mnkoxeaMzLgfCxUdDSZW-78765865": "6ad969956a956d309f69e3711510f72c0ef5e0e3d96033df505bcf049cdc0657", "TT_test_passphrase_tt.py::test_passphrase_input[Y@14lw%p)JN@f54MYvys@zj'g-mnkoxeaMzLgfCxUdDSZW-78765865": "03d200ce4735c3d7bba2549fef0a5b24a0c5b4a35c05a6aaaebf5121a8ac9cb5",
"TT_test_passphrase_tt.py::test_passphrase_input[abc 123-mvqzZUb9NaUc62Buk9WCP4L7hunsXFyamT]": "d165ad086b7f8abe0d4e678e578703c2c930601f4a845896613ef596a1ed51eb", "TT_test_passphrase_tt.py::test_passphrase_input[abc 123-mvqzZUb9NaUc62Buk9WCP4L7hunsXFyamT]": "7aac85ab8a1f2fbabd72a5133d030aab38b140ea7e58025b285ade367bb3d9b5",
"TT_test_passphrase_tt.py::test_passphrase_input[abc123ABC_<>-mtHHfh6uHtJiACwp7kzJZ97yueT6sEdQiG]": "759fb6628f9fb532e1dcfd764be5689ea57ababc4b58bf8aca5ea4e6eab7a8fe", "TT_test_passphrase_tt.py::test_passphrase_input[abc123ABC_<>-mtHHfh6uHtJiACwp7kzJZ97yueT6sEdQiG]": "5751b5c27df31536887a3dd19f2684b3e4d6b230fba7949320c4e46eb0691e3a",
"TT_test_passphrase_tt.py::test_passphrase_input[dadadadadadadadadadadadadadadadadadadadadadada-1cc97541": "dcd3bc20a4caa908625b39f4cc3a7d33e47f35ce6531a52fc31bd90e0b12a67d", "TT_test_passphrase_tt.py::test_passphrase_input[dadadadadadadadadadadadadadadadadadadadadadada-1cc97541": "79005c64239d61e7fb692f709b5fee55371636340c8a123ce97040e23d607875",
"TT_test_passphrase_tt.py::test_passphrase_input[dadadadadadadadadadadadadadadadadadadadadadada-ca475dad": "1da0831b4ee13b7bc3df0de07a539822103437adb6e7f159aabf1519cb9c2019", "TT_test_passphrase_tt.py::test_passphrase_input[dadadadadadadadadadadadadadadadadadadadadadada-ca475dad": "c2b2081575601168db7dea4eaf0ec93f3b0bf73088eeafe98fd05ec3f02de00e",
"TT_test_passphrase_tt.py::test_passphrase_input_over_50_chars": "dcd3bc20a4caa908625b39f4cc3a7d33e47f35ce6531a52fc31bd90e0b12a67d", "TT_test_passphrase_tt.py::test_passphrase_input_over_50_chars": "79005c64239d61e7fb692f709b5fee55371636340c8a123ce97040e23d607875",
"TT_test_passphrase_tt.py::test_passphrase_long_spaces_deletion": "82a90342be1b83f51219d9faec25c7dbf83733a99a6a535791015d30d40485bf", "TT_test_passphrase_tt.py::test_passphrase_long_spaces_deletion": "a2c0a9ea72534e69351581ef76a3c980ca95ccedf63b0e0e6a6d89a5a00b6df2",
"TT_test_passphrase_tt.py::test_passphrase_loop_all_characters": "d29136fd1fcb4bd5575b9225e30b455e7a0798bb0887dcdf17a689dfe485fdb3", "TT_test_passphrase_tt.py::test_passphrase_loop_all_characters": "c166a018c27a2d1037dfe309485f981c02eaf72b03f33e90eb24439ba310ff0a",
"TT_test_passphrase_tt.py::test_passphrase_prompt_disappears": "4b16a81abd2c3eef2263e97952c535b40b1b64e4b175285a73135b0c675a4d9e", "TT_test_passphrase_tt.py::test_passphrase_prompt_disappears": "df5d06a0e97d1ece6374f7e2fd423cf50953f377fbf4c2751f7e7b5e2706d1b1",
"TT_test_pin.py::test_pin_cancel": "8f40ce02fb129962837393ffcd6ad19e07a3e81870b4e50205f2b64f9c8b1cf1", "TT_test_pin.py::test_pin_cancel": "896edbb1b6fc3b35db5f066004660403c9aa4228e1d313a373e225e6cdfa0433",
"TT_test_pin.py::test_pin_change": "0f6951c2354ae02b5fa1df0ae030093d259bea4e1e962100da1900259cc8e830", "TT_test_pin.py::test_pin_change": "e3c858f93ba6fde78b42ffda52de687e40f82db68468dfcfe19a417f1ebba2d9",
"TT_test_pin.py::test_pin_incorrect": "8daa7625b896f4faa3c9dd4f8a254af1cc6b66fc71edd2a0d968db1ee424f5d9", "TT_test_pin.py::test_pin_incorrect": "1c146baaa485871f80d0a1a2a01cf05401b252679a5f6ab7c72a651ef3810d58",
"TT_test_pin.py::test_pin_long": "34692638906727bd3ebf89468aa8d4658866f1aac7775e8cae28bb337d588b3d", "TT_test_pin.py::test_pin_long": "c6da95e5e60f26feff227a1b3ce5d9b11cabaa44e613abffde3bb91b4ddcfac0",
"TT_test_pin.py::test_pin_long_delete": "eea0b6485700cce2bc6c13e6d1971e4add11355aa155edf2fcb9bb699dfb0654", "TT_test_pin.py::test_pin_long_delete": "fa7c90ec909a47c3d2fec91d185c0bab288b91803719094f8a3bac88095aedd0",
"TT_test_pin.py::test_pin_longer_than_max": "6a7e55fd6d3ff117ee5deb40f1c07cf8d3248931a29e4de6498d813744af2981", "TT_test_pin.py::test_pin_longer_than_max": "a7b78e1514f564a3f38b005f099324773c2c57f2c6dfc5afc4afb82dda09f6cc",
"TT_test_pin.py::test_pin_same_as_wipe_code": "f09497fa686f884706818c0c850db9c351b5cd30ebc60f9aab11c62b75714ebe", "TT_test_pin.py::test_pin_same_as_wipe_code": "ce5542e0126035b0a11c2c306fb4bb7d2c6b9ed3f0544986dd8a74e9f82eb698",
"TT_test_pin.py::test_pin_setup": "470ba3c1e991f14b99ef0256774dcb25ec8efa2e24951f923e9439e1f5d9e71d", "TT_test_pin.py::test_pin_setup": "2ff5a373d2772ccf4e55c05d07d06b9edfa303f465968be90022c77e81022a2d",
"TT_test_pin.py::test_pin_setup_mismatch": "bc3d883b79a2edef4cc38f0284afe8afcb5f7cc49d601a371a131859bfb628cb", "TT_test_pin.py::test_pin_setup_mismatch": "d9f6d7d66e92c9000bd0e5db533e4e94e9a59788b278952f793eaea5d579f2da",
"TT_test_pin.py::test_pin_short": "b5377990e4a1f324133601e3ca4726cde7af50b3e2c3f53738022ed9108a6a79", "TT_test_pin.py::test_pin_short": "ba6538602cf63d2776e17f6d9b9a3fd7cb5fdf5b6a6bbd1debe21a12af597896",
"TT_test_pin.py::test_wipe_code_same_as_pin": "91e501a4bd812ddaf9d39bfb86ab37fa18c75f33ec903d26ccbb30e5e3148f49", "TT_test_pin.py::test_wipe_code_same_as_pin": "507126101e78e316d030a21f6dfc36f76a88be0cca0251442ae78b96b93e766a",
"TT_test_pin.py::test_wipe_code_setup": "7132ac8f27171c3c916047e0f93bfcf1703eafec3bdc5dc02c8e1184351f5bd2", "TT_test_pin.py::test_wipe_code_setup": "d3b9ba77ff4110d5ff191188a577203d956d4ac07c178316cb053b432116e777",
"TT_test_recovery.py::test_recovery_bip39": "614e9204c01c379b4b88ba618addd50f22cf9f75492f2363cdca14acee1c883f", "TT_test_recovery.py::test_recovery_bip39": "a5ef33d7408e4a78ab3070dd56ceb4316d5505903f2c4f4a2a6935f26ac8e9f9",
"TT_test_recovery.py::test_recovery_slip39_basic": "73e2c4e4c4cb75206a5b739e4196035a4a3eef83a434a880ac27a8c7b5a3a0e2", "TT_test_recovery.py::test_recovery_slip39_basic": "2c983e0c87f50ac850b68872aeedd65d05966c2497ac65134c2a45851ff62dcd",
"TT_test_reset_bip39.py::test_reset_bip39": "01547ca97308b6b3b54fab9c8f18d4d43a8ca0637a95f4de6e0690808342fe4c", "TT_test_reset_bip39.py::test_reset_bip39": "0f4b7588543acd9dfae3a366d872ba55bb3783302f08cc72f10e41510495a484",
"TT_test_reset_slip39_advanced.py::test_reset_slip39_advanced_16of16groups_16of16shares": "4ddcb8eeb9062e739926998abb0bbed918b63cf1065c61199194f99034d54968", "TT_test_reset_slip39_advanced.py::test_reset_slip39_advanced_16of16groups_16of16shares": "e90a492431f6e481eb44661382ab08eb594e2a5319e73055e26a7cfc04df4dba",
"TT_test_reset_slip39_advanced.py::test_reset_slip39_advanced_2of2groups_2of2shares": "37f5730dc1da45ef31792733a9272626ab070c8245d95b062df8695a6a9cf6c7", "TT_test_reset_slip39_advanced.py::test_reset_slip39_advanced_2of2groups_2of2shares": "9a499f4dea0fcfb98e1cc33130f7b5093d0f70c474f38331aedaf37e665b1385",
"TT_test_reset_slip39_basic.py::test_reset_slip39_basic_16of16": "1a1d30da89b002e082c03fca80c99d515fee1ff144ec01a24f8bcfd4cf831ec3", "TT_test_reset_slip39_basic.py::test_reset_slip39_basic_16of16": "ee4fd329bd3b518e5e0551a52f1505d3aa60b42474166eb06972789105191fbb",
"TT_test_reset_slip39_basic.py::test_reset_slip39_basic_1of1": "ffc727ca4ec65738618ed20fcf0bd91c6d693d22575ab9a56c8c1bab1c66b3ec" "TT_test_reset_slip39_basic.py::test_reset_slip39_basic_1of1": "a41a660e0cf3b964c1a8c83e96acbb3a0eecaf9bce69f9c4ac267585bc3b4b12"
}, },
"device_tests": { "device_tests": {
"TT_binance-test_get_address.py::test_binance_get_address[m-44h-714h-0h-0-0-bnb1hgm0p7khfk85zpz-68e2cb5a": "483ff25f0ff24de80631dfb202ac681c38dfbf44c5905505281c2d0719a94fc6", "TT_binance-test_get_address.py::test_binance_get_address[m-44h-714h-0h-0-0-bnb1hgm0p7khfk85zpz-68e2cb5a": "483ff25f0ff24de80631dfb202ac681c38dfbf44c5905505281c2d0719a94fc6",

Loading…
Cancel
Save