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.ui import display
from trezor.enums import MessageType, DebugPhysicalButton
from trezor.enums import MessageType
from trezor.messages import (
DebugLinkLayout,
Success,
@ -33,20 +33,16 @@ if __debug__:
DebugLinkWatchLayout,
)
reset_current_words = loop.chan()
reset_word_index = loop.chan()
confirm_chan = loop.chan()
swipe_chan = loop.chan()
input_chan = loop.chan()
model_r_btn_chan = loop.chan()
confirm_signal = confirm_chan.take
result_chan = loop.chan()
button_chan = loop.chan()
click_chan = loop.chan()
swipe_signal = swipe_chan.take
input_signal = input_chan.take
model_r_btn_signal = model_r_btn_chan.take
synthetic_event = loop.chan()
synthetic_event_signal = synthetic_event.take
result_signal = result_chan.take
button_signal = button_chan.take
click_signal = click_chan.take
debuglink_decision_chan = loop.chan()
@ -74,21 +70,18 @@ if __debug__:
event_id: int | None, msg: DebugLinkDecision
) -> None:
from trezor.enums import DebugButton
from trezor.ui import Result
if msg.button is not None:
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:
await confirm_chan.put(Result(trezorui2.CONFIRMED))
await result_chan.put((event_id, trezorui2.CONFIRMED))
elif msg.button == DebugButton.INFO:
await confirm_chan.put(Result(trezorui2.INFO))
if msg.physical_button is not None:
await model_r_btn_chan.put(msg.physical_button)
await result_chan.put((event_id, trezorui2.INFO))
if msg.input is not None:
await result_chan.put((event_id, msg.input))
if msg.swipe is not None:
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:
while True:
@ -105,12 +98,12 @@ if __debug__:
while True:
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
break
elif event_id == awaited_event_id:
# We found what we were waiting for
storage.awaited_event = None
debug_events.awaited_event = None
break
elif event_id > awaited_event_id:
# Sanity check
@ -136,21 +129,6 @@ if __debug__:
await DEBUG_CONTEXT.write(DebugLinkState(layout_lines=content))
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(
ctx: wire.Context, msg: DebugLinkWatchLayout
) -> Success:
@ -161,7 +139,10 @@ if __debug__:
# analyze the resulted layout.
# Resetting the debug events makes sure that the previous
# 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()
if msg.watch:
@ -173,7 +154,7 @@ if __debug__:
async def dispatch_DebugLinkDecision(
ctx: wire.Context, msg: DebugLinkDecision
) -> None:
from trezor import io, workflow
from trezor import workflow
workflow.idle_timer.touch()
@ -183,42 +164,21 @@ if __debug__:
x = msg.x # 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
# TT click on specific coordinates, with possible hold
if x is not None and y is not None and utils.MODEL in ("T",):
# Getting IDs and incrementing the counter for next events
# Sending two events - click down and click up
first_evt_id = event_id
second_evt_id = debug_events.last_event + 1
debug_events.last_event += 1
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))
click_chan.publish((debug_events.last_event, x, y, msg.hold_ms))
# TR press specific button
elif msg.physical_button is not None and utils.MODEL in ("R",):
button_chan.publish(
(debug_events.last_event, msg.physical_button, msg.hold_ms)
)
# Something more general
else:
# 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:
# We wait for all the previously sent events
@ -251,8 +211,6 @@ if __debug__:
if msg.wait_word_pos:
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
async def dispatch_DebugLinkRecordScreen(

@ -57,71 +57,95 @@ class RustLayout(ui.Layout):
from trezor.enums import DebugPhysicalButton
def create_tasks(self) -> tuple[loop.AwaitableTask, ...]:
from apps.debug import confirm_signal, input_signal
return (
self.handle_input_and_rendering(),
self.handle_timers(),
self.handle_swipe(),
self.handle_button_click(),
confirm_signal(),
input_signal(),
self.handle_swipe_signal(),
self.handle_button_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()
# 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]:
"""Gets the visible content of the screen."""
self._place_layout()
self._place_layout() # TODO: is this necessary?
raw = self._read_content_raw()
return " ".join(raw).split("\n")
def _press_left(self) -> Any:
async def _press_left(self, hold_ms: int | None) -> Any:
"""Triggers left button press."""
self.layout.button_event(io.BUTTON_PRESSED, io.BUTTON_LEFT)
self._paint()
if hold_ms is not None:
await loop.sleep(hold_ms)
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."""
self.layout.button_event(io.BUTTON_PRESSED, io.BUTTON_RIGHT)
self._paint()
if hold_ms is not None:
await loop.sleep(hold_ms)
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."""
self.layout.button_event(io.BUTTON_PRESSED, io.BUTTON_LEFT)
self._paint()
self.layout.button_event(io.BUTTON_PRESSED, io.BUTTON_RIGHT)
self._paint()
if hold_ms is not None:
await loop.sleep(hold_ms)
self.layout.button_event(io.BUTTON_RELEASED, io.BUTTON_LEFT)
self._paint()
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 import workflow
from apps.debug import notify_layout_change
from storage import debug as debug_storage
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:
msg = self._press_middle()
msg = await self._press_middle(hold_ms)
elif btn_to_press == DebugPhysicalButton.RIGHT_BTN:
msg = self._press_right()
msg = await self._press_right(hold_ms)
else:
raise Exception(f"Unknown button: {btn_to_press}")
self.layout.paint()
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)
# So that these presses will keep trezor awake
# (it will not be locked after auto_lock_delay_ms)
workflow.idle_timer.touch()
ui.refresh() # so that a screenshot is taken
notify_layout_change(self)
self._paint()
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.
Only `UP` and `DOWN` directions are supported.
@ -135,9 +159,9 @@ class RustLayout(ui.Layout):
else:
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.
Waits for `swipe_signal` and carries it out.
@ -145,19 +169,19 @@ class RustLayout(ui.Layout):
from apps.debug import swipe_signal
while True:
direction = await swipe_signal()
self._swipe(direction)
event_id, direction = await swipe_signal()
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.
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:
btn = await model_r_btn_signal()
self._press_button(btn)
event_id, btn, hold_ms = await button_signal()
await self._press_button(event_id, btn, hold_ms)
else:
@ -172,12 +196,21 @@ class RustLayout(ui.Layout):
if __debug__ and self.should_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.
# (handle_rendering might be called multiple times in a single await,
# because of the endless loop in __iter__)
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.
ui.backlight_fade(self.BACKLIGHT_LEVEL)

@ -31,7 +31,11 @@ class HomescreenBase(RustLayout):
# In __debug__ mode, ignore {confirm,swipe,input}_signal.
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):

@ -56,16 +56,27 @@ class RustLayout(ui.Layout):
if __debug__:
def create_tasks(self) -> tuple[loop.AwaitableTask, ...]:
from apps.debug import confirm_signal, input_signal
return (
self.handle_timers(),
self.handle_input_and_rendering(),
self.handle_swipe(),
confirm_signal(),
input_signal(),
self.handle_click_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]:
result: list[str] = []
@ -103,6 +114,45 @@ class RustLayout(ui.Layout):
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:
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]
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)
self._first_paint()
while True:
# Using `yield` instead of `await` to avoid allocations.
event_id: int | None = None
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
event, x, y = yield touch
workflow.idle_timer.touch()
msg = None
if event in (io.TOUCH_START, io.TOUCH_MOVE, io.TOUCH_END):
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:
raise ui.Result(msg)
self._paint()

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

@ -31,7 +31,11 @@ class HomescreenBase(RustLayout):
# In __debug__ mode, ignore {confirm,swipe,input}_signal.
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):

@ -668,16 +668,13 @@ class DebugLink:
)
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]:
hold_ms = hold_ms + extra_ms # safety margin
result = self.input(
return self.input(
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:
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
elif debug.model == "R":
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:
@ -54,6 +56,8 @@ def go_back(debug: "DebugLink", wait: bool = False) -> "LayoutContent" | None:
return debug.click(buttons.CANCEL, wait=wait) # type: ignore
elif debug.model == "R":
return debug.press_left(wait=wait) # type: ignore
else:
raise ValueError(f"Unknown model {debug.model}")
def navigate_to_action_and_press(

@ -112,13 +112,15 @@ def read_words(
words.extend(layout.seed_words())
# 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
# each word set
if do_htc:
debug.click(buttons.OK, hold_ms=1500, wait=True)
else:
debug.press_yes()
debug.press_yes()
return words

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

@ -14,7 +14,6 @@
# 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 time
from typing import TYPE_CHECKING
import pytest
@ -37,10 +36,9 @@ def test_hold_to_lock(device_handler: "BackgroundDeviceHandler"):
def hold(duration: int, wait: bool = True) -> None:
if debug.model == "R":
debug.press_right_htc(hold_ms=duration)
debug.press_right_htc(hold_ms=duration, wait=wait)
else:
debug.input(x=13, y=37, hold_ms=duration, wait=wait)
time.sleep(duration / 1000 + 0.5)
assert device_handler.features().unlocked is False

@ -119,7 +119,9 @@ def _slip39_advanced_reset(
for _ in range(group_count):
for _ in range(share_count):
# 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
reset.confirm_words(debug, words)

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

@ -200,6 +200,9 @@ def client(
request.session.shouldstop = "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:
# we need to reseed before the wipe
_raw_client.debug.reseed(0)

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

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

Loading…
Cancel
Save