1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2024-11-10 09:39:00 +00:00

fix(tests): restore test functionality on legacy

The global layout related changes were wrong for T1 where debuglink
behavior is significantly different; in particular, it is not always
possible to communicate over debuglink.

This change reverts to the old behavior for T1B1 and keeps the new one
only for core-based models.
This commit is contained in:
matejcik 2024-04-11 13:49:27 +02:00 committed by M1nd3r
parent 12a59b88a5
commit 680af2cf18
2 changed files with 68 additions and 10 deletions

View File

@ -624,7 +624,7 @@ class DebugLink:
wait_type = DebugWaitType.IMMEDIATE wait_type = DebugWaitType.IMMEDIATE
else: else:
wait_type = self.input_wait_type wait_type = self.input_wait_type
return self.snapshot(wait_type) return self._snapshot_core(wait_type)
press_yes = _make_input_func(button=messages.DebugButton.YES) press_yes = _make_input_func(button=messages.DebugButton.YES)
"""Confirm current layout. See `_decision` for more details.""" """Confirm current layout. See `_decision` for more details."""
@ -667,10 +667,14 @@ class DebugLink:
messages.DebugLinkDecision(x=x, y=y, hold_ms=hold_ms), wait messages.DebugLinkDecision(x=x, y=y, hold_ms=hold_ms), wait
) )
def snapshot( def _snapshot_core(
self, wait_type: DebugWaitType = DebugWaitType.IMMEDIATE self, wait_type: DebugWaitType = DebugWaitType.IMMEDIATE
) -> LayoutContent: ) -> LayoutContent:
"""Save text and image content of the screen to relevant directories.""" """Save text and image content of the screen to relevant directories."""
# skip the snapshot if we are on T1
if self.model is models.T1B1:
return LayoutContent([])
# take the snapshot # take the snapshot
state = self.state(wait_type) state = self.state(wait_type)
layout = LayoutContent(state.tokens) layout = LayoutContent(state.tokens)
@ -678,8 +682,6 @@ class DebugLink:
if state.tokens and self.layout_dirty: if state.tokens and self.layout_dirty:
# save it, unless we already did or unless it's empty # save it, unless we already did or unless it's empty
self.save_debug_screen(layout.visible_screen()) self.save_debug_screen(layout.visible_screen())
if state.layout is not None:
self.save_screenshot(state.layout)
self.layout_dirty = False self.layout_dirty = False
# return the layout # return the layout
@ -748,7 +750,16 @@ class DebugLink:
def erase_sd_card(self, format: bool = True) -> messages.Success: def erase_sd_card(self, format: bool = True) -> messages.Success:
return self._call(messages.DebugLinkEraseSdCard(format=format)) return self._call(messages.DebugLinkEraseSdCard(format=format))
def save_screenshot(self, data: bytes) -> None: def snapshot_legacy(self) -> None:
"""Snapshot the current state of the device."""
if self.model is not models.T1B1:
return
state = self.state()
if state.layout is not None:
self._save_screenshot_t1(state.layout)
def _save_screenshot_t1(self, data: bytes) -> None:
if self.t1_screenshot_directory is None: if self.t1_screenshot_directory is None:
return return
@ -833,7 +844,7 @@ class DebugUI:
self.debuglink.press_yes() self.debuglink.press_yes()
def button_request(self, br: messages.ButtonRequest) -> None: def button_request(self, br: messages.ButtonRequest) -> None:
self.debuglink.snapshot() self.debuglink.snapshot_legacy()
if self.input_flow is None: if self.input_flow is None:
self._default_input_flow(br) self._default_input_flow(br)
@ -847,7 +858,7 @@ class DebugUI:
self.input_flow = self.INPUT_FLOW_DONE self.input_flow = self.INPUT_FLOW_DONE
def get_pin(self, code: Optional["PinMatrixRequestType"] = None) -> str: def get_pin(self, code: Optional["PinMatrixRequestType"] = None) -> str:
self.debuglink.snapshot() self.debuglink.snapshot_legacy()
if self.pins is None: if self.pins is None:
raise RuntimeError("PIN requested but no sequence was configured") raise RuntimeError("PIN requested but no sequence was configured")
@ -858,7 +869,7 @@ class DebugUI:
raise AssertionError("PIN sequence ended prematurely") raise AssertionError("PIN sequence ended prematurely")
def get_passphrase(self, available_on_device: bool) -> str: def get_passphrase(self, available_on_device: bool) -> str:
self.debuglink.snapshot() self.debuglink.snapshot_legacy()
return self.passphrase return self.passphrase
@ -1254,6 +1265,29 @@ class TrezorClientDebugLink(TrezorClient):
output.append(textwrap.indent(protobuf.format_message(act), " ")) output.append(textwrap.indent(protobuf.format_message(act), " "))
raise AssertionError("\n".join(output)) raise AssertionError("\n".join(output))
def sync_responses(self) -> None:
"""Synchronize Trezor device receiving with caller.
When a failed test does not read out the response, the next caller will write
a request, but read the previous response -- while the device had already sent
and placed into queue the new response.
This function will call `Ping` and read responses until it locates a `Success`
with the expected text. This means that we are reading up-to-date responses.
"""
import secrets
# Start by canceling whatever is on screen. This will work to cancel T1 PIN
# prompt, which is in TINY mode and does not respond to `Ping`.
# go to super() to avoid message filtering
super()._raw_write(messages.Cancel())
message = "SYNC" + secrets.token_hex(8)
super()._raw_write(messages.Ping(message=message))
resp = None
while resp != messages.Success(message=message):
resp = super()._raw_read()
def mnemonic_callback(self, _) -> str: def mnemonic_callback(self, _) -> str:
word, pos = self.debug.read_recovery_word() word, pos = self.debug.read_recovery_word()
if word: if word:

View File

@ -61,8 +61,8 @@ def test_busy_state(client: Client):
assert client.features.unlocked is True assert client.features.unlocked is True
@pytest.mark.flaky(max_runs=5) @pytest.mark.skip_t1b1
def test_busy_expiry(client: Client): def test_busy_expiry_core(client: Client):
WAIT_TIME_MS = 1500 WAIT_TIME_MS = 1500
TOLERANCE = 1000 TOLERANCE = 1000
@ -85,3 +85,27 @@ def test_busy_expiry(client: Client):
# Also needs to come back to Homescreen (for UI tests). # Also needs to come back to Homescreen (for UI tests).
client.refresh_features() client.refresh_features()
_assert_busy(client, False) _assert_busy(client, False)
@pytest.mark.flaky(max_runs=5)
def test_busy_expiry_legacy(client: Client):
if client.model is not models.T1B1:
# TODO better skip markers
pytest.skip("Test only for T1B1")
_assert_busy(client, False)
# Show the busy dialog.
device.set_busy(client, expiry_ms=1500)
_assert_busy(client, True)
# Hasn't expired yet.
time.sleep(0.1)
_assert_busy(client, True)
# Wait for it to expire. Add some tolerance to account for CI/hardware slowness.
time.sleep(4.0)
# Check that the device is no longer busy.
# Also needs to come back to Homescreen (for UI tests).
client.refresh_features()
_assert_busy(client, False)