1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2024-12-24 07:18:09 +00:00

feat(core): enable UI tests for redesigned UI

[no changelog]
This commit is contained in:
Martin Milata 2022-04-05 14:35:37 +02:00
parent 2f987c3c5e
commit 1f612580f7
10 changed files with 1093 additions and 14 deletions

View File

@ -101,10 +101,10 @@ test_emu_click: ## run click tests
$(EMU_TEST) $(PYTEST) $(TESTPATH)/click_tests $(TESTOPTS)
test_emu_ui: ## run ui integration tests
$(EMU_TEST) $(PYTEST) $(TESTPATH)/device_tests --ui=test --ui-check-missing $(TESTOPTS)
UI2="$(UI2)" $(EMU_TEST) $(PYTEST) $(TESTPATH)/device_tests --ui=test --ui-check-missing $(TESTOPTS)
test_emu_ui_record: ## record and hash screens for ui integration tests
$(EMU_TEST) $(PYTEST) $(TESTPATH)/device_tests --ui=record --ui-check-missing $(TESTOPTS)
UI2="$(UI2)" $(EMU_TEST) $(PYTEST) $(TESTPATH)/device_tests --ui=record --ui-check-missing $(TESTOPTS)
pylint: ## run pylint on application sources and tests
pylint -E $(shell find src tests -name *.py)

View File

@ -15,6 +15,7 @@ static void _librust_qstrs(void) {
MP_QSTR_Layout;
MP_QSTR_CONFIRMED;
MP_QSTR_CANCELLED;
MP_QSTR_INFO;
MP_QSTR_confirm_action;
MP_QSTR_confirm_text;
MP_QSTR_request_pin;

View File

@ -220,24 +220,30 @@ impl LayoutObj {
fn symbol(&mut self, name: &str) {
self.0
.call_with_n_args(&[name.try_into().unwrap()])
.call_with_n_args(&[
"<".try_into().unwrap(),
name.try_into().unwrap(),
">".try_into().unwrap(),
])
.unwrap();
}
fn open(&mut self, name: &str) {
self.0
.call_with_n_args(&[name.try_into().unwrap()])
.call_with_n_args(&["<".try_into().unwrap(), name.try_into().unwrap()])
.unwrap();
}
fn field(&mut self, name: &str, value: &dyn Trace) {
self.0
.call_with_n_args(&[name.try_into().unwrap()])
.call_with_n_args(&[name.try_into().unwrap(), ": ".try_into().unwrap()])
.unwrap();
value.trace(self);
}
fn close(&mut self) {}
fn close(&mut self) {
self.0.call_with_n_args(&[">".try_into().unwrap()]).unwrap();
}
}
self.inner

View File

@ -25,6 +25,7 @@ unsafe impl Sync for ResultObj {}
static CONFIRMED_TYPE: Type = obj_type! { name: Qstr::MP_QSTR_CONFIRMED, };
static CANCELLED_TYPE: Type = obj_type! { name: Qstr::MP_QSTR_CANCELLED, };
static INFO_TYPE: Type = obj_type! { name: Qstr::MP_QSTR_INFO, };
pub static CONFIRMED: ResultObj = ResultObj {
base: CONFIRMED_TYPE.as_base(),
@ -32,3 +33,6 @@ pub static CONFIRMED: ResultObj = ResultObj {
pub static CANCELLED: ResultObj = ResultObj {
base: CANCELLED_TYPE.as_base(),
};
pub static INFO: ResultObj = ResultObj {
base: INFO_TYPE.as_base(),
};

View File

@ -12,7 +12,7 @@ use crate::{
},
layout::{
obj::{ComponentMsgObj, LayoutObj},
result::{CANCELLED, CONFIRMED},
result::{CANCELLED, CONFIRMED, INFO},
},
},
util,
@ -212,6 +212,9 @@ pub static mp_module_trezorui2: Module = obj_module! {
/// CANCELLED: object
Qstr::MP_QSTR_CANCELLED => CANCELLED.as_obj(),
/// INFO: object
Qstr::MP_QSTR_INFO => INFO.as_obj(),
/// def confirm_action(
/// *,
/// title: str,

View File

@ -27,6 +27,7 @@ def confirm_text(
"""Confirm text."""
CONFIRMED: object
CANCELLED: object
INFO: object
# rust/src/ui/model_tt/layout.rs

View File

@ -50,6 +50,13 @@ if __debug__:
LAYOUT_WATCHER_STATE = 1
LAYOUT_WATCHER_LAYOUT = 2
try:
import trezorui2
UI2 = True
except ImportError:
UI2 = False
def screenshot() -> bool:
if storage.save_screen:
display.save(storage.save_screen_directory + "/refresh-")
@ -62,10 +69,20 @@ if __debug__:
layout_change_chan.publish(storage.current_content)
async def dispatch_debuglink_decision(msg: DebugLinkDecision) -> None:
from trezor.enums import DebugButton
from trezor.enums import DebugSwipeDirection
from trezor.enums import DebugButton, DebugSwipeDirection
from trezor.ui import Result
from trezor.ui.components.tt import confirm, swipe
if UI2:
confirm = trezorui2
class swipe:
SWIPE_UP = 0x01
SWIPE_DOWN = 0x02
SWIPE_LEFT = 0x04
SWIPE_RIGHT = 0x08
else:
from trezor.ui.components.tt import confirm, swipe
if msg.button is not None:
if msg.button == DebugButton.NO:

View File

@ -24,11 +24,47 @@ class _RustLayout(ui.Layout):
def set_timer(self, token: int, deadline: int) -> None:
self.timer.schedule(deadline, token)
def create_tasks(self) -> tuple[loop.Task, ...]:
return self.handle_input_and_rendering(), self.handle_timers()
if __debug__:
def create_tasks(self) -> tuple[loop.AwaitableTask, ...]:
from apps.debug import confirm_signal, input_signal
return (
self.handle_input_and_rendering(),
self.handle_timers(),
confirm_signal(),
input_signal(),
)
def read_content(self) -> list[str]:
result = []
def callback(*args):
for arg in args:
result.append(str(arg))
self.layout.trace(callback)
result = " ".join(result).split("\n")
return result
else:
def create_tasks(self) -> tuple[loop.AwaitableTask, ...]:
return self.handle_input_and_rendering(), self.handle_timers()
def _before_render(self) -> None:
if __debug__ and self.should_notify_layout_change:
from apps.debug import notify_layout_change
# 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)
def handle_input_and_rendering(self) -> loop.Task: # type: ignore [awaitable-is-generator]
touch = loop.wait(io.TOUCH)
self._before_render()
ui.display.clear()
self.layout.paint()
# self.layout.bounds()

View File

@ -1,5 +1,6 @@
import hashlib
import json
import os
import re
import shutil
from contextlib import contextmanager
@ -112,6 +113,9 @@ def screen_recording(
# Making the model global for other functions
global MODEL
MODEL = f"T{client.features.model}"
if os.getenv("UI2") == "1":
MODEL += "ui2"
test_name = f"{MODEL}_{test_name}"
screens_test_path = SCREENS_DIR / test_name

File diff suppressed because it is too large Load Diff