mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-07-05 14:22:33 +00:00
feat(core): enable UI tests for redesigned UI
[no changelog]
This commit is contained in:
parent
2f987c3c5e
commit
1f612580f7
@ -101,10 +101,10 @@ test_emu_click: ## run click tests
|
|||||||
$(EMU_TEST) $(PYTEST) $(TESTPATH)/click_tests $(TESTOPTS)
|
$(EMU_TEST) $(PYTEST) $(TESTPATH)/click_tests $(TESTOPTS)
|
||||||
|
|
||||||
test_emu_ui: ## run ui integration tests
|
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
|
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: ## run pylint on application sources and tests
|
||||||
pylint -E $(shell find src tests -name *.py)
|
pylint -E $(shell find src tests -name *.py)
|
||||||
|
@ -15,6 +15,7 @@ static void _librust_qstrs(void) {
|
|||||||
MP_QSTR_Layout;
|
MP_QSTR_Layout;
|
||||||
MP_QSTR_CONFIRMED;
|
MP_QSTR_CONFIRMED;
|
||||||
MP_QSTR_CANCELLED;
|
MP_QSTR_CANCELLED;
|
||||||
|
MP_QSTR_INFO;
|
||||||
MP_QSTR_confirm_action;
|
MP_QSTR_confirm_action;
|
||||||
MP_QSTR_confirm_text;
|
MP_QSTR_confirm_text;
|
||||||
MP_QSTR_request_pin;
|
MP_QSTR_request_pin;
|
||||||
|
@ -220,24 +220,30 @@ impl LayoutObj {
|
|||||||
|
|
||||||
fn symbol(&mut self, name: &str) {
|
fn symbol(&mut self, name: &str) {
|
||||||
self.0
|
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();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn open(&mut self, name: &str) {
|
fn open(&mut self, name: &str) {
|
||||||
self.0
|
self.0
|
||||||
.call_with_n_args(&[name.try_into().unwrap()])
|
.call_with_n_args(&["<".try_into().unwrap(), name.try_into().unwrap()])
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn field(&mut self, name: &str, value: &dyn Trace) {
|
fn field(&mut self, name: &str, value: &dyn Trace) {
|
||||||
self.0
|
self.0
|
||||||
.call_with_n_args(&[name.try_into().unwrap()])
|
.call_with_n_args(&[name.try_into().unwrap(), ": ".try_into().unwrap()])
|
||||||
.unwrap();
|
.unwrap();
|
||||||
value.trace(self);
|
value.trace(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn close(&mut self) {}
|
fn close(&mut self) {
|
||||||
|
self.0.call_with_n_args(&[">".try_into().unwrap()]).unwrap();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.inner
|
self.inner
|
||||||
|
@ -25,6 +25,7 @@ unsafe impl Sync for ResultObj {}
|
|||||||
|
|
||||||
static CONFIRMED_TYPE: Type = obj_type! { name: Qstr::MP_QSTR_CONFIRMED, };
|
static CONFIRMED_TYPE: Type = obj_type! { name: Qstr::MP_QSTR_CONFIRMED, };
|
||||||
static CANCELLED_TYPE: Type = obj_type! { name: Qstr::MP_QSTR_CANCELLED, };
|
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 {
|
pub static CONFIRMED: ResultObj = ResultObj {
|
||||||
base: CONFIRMED_TYPE.as_base(),
|
base: CONFIRMED_TYPE.as_base(),
|
||||||
@ -32,3 +33,6 @@ pub static CONFIRMED: ResultObj = ResultObj {
|
|||||||
pub static CANCELLED: ResultObj = ResultObj {
|
pub static CANCELLED: ResultObj = ResultObj {
|
||||||
base: CANCELLED_TYPE.as_base(),
|
base: CANCELLED_TYPE.as_base(),
|
||||||
};
|
};
|
||||||
|
pub static INFO: ResultObj = ResultObj {
|
||||||
|
base: INFO_TYPE.as_base(),
|
||||||
|
};
|
||||||
|
@ -12,7 +12,7 @@ use crate::{
|
|||||||
},
|
},
|
||||||
layout::{
|
layout::{
|
||||||
obj::{ComponentMsgObj, LayoutObj},
|
obj::{ComponentMsgObj, LayoutObj},
|
||||||
result::{CANCELLED, CONFIRMED},
|
result::{CANCELLED, CONFIRMED, INFO},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
util,
|
util,
|
||||||
@ -212,6 +212,9 @@ pub static mp_module_trezorui2: Module = obj_module! {
|
|||||||
/// CANCELLED: object
|
/// CANCELLED: object
|
||||||
Qstr::MP_QSTR_CANCELLED => CANCELLED.as_obj(),
|
Qstr::MP_QSTR_CANCELLED => CANCELLED.as_obj(),
|
||||||
|
|
||||||
|
/// INFO: object
|
||||||
|
Qstr::MP_QSTR_INFO => INFO.as_obj(),
|
||||||
|
|
||||||
/// def confirm_action(
|
/// def confirm_action(
|
||||||
/// *,
|
/// *,
|
||||||
/// title: str,
|
/// title: str,
|
||||||
|
@ -27,6 +27,7 @@ def confirm_text(
|
|||||||
"""Confirm text."""
|
"""Confirm text."""
|
||||||
CONFIRMED: object
|
CONFIRMED: object
|
||||||
CANCELLED: object
|
CANCELLED: object
|
||||||
|
INFO: object
|
||||||
|
|
||||||
|
|
||||||
# rust/src/ui/model_tt/layout.rs
|
# rust/src/ui/model_tt/layout.rs
|
||||||
|
@ -50,6 +50,13 @@ if __debug__:
|
|||||||
LAYOUT_WATCHER_STATE = 1
|
LAYOUT_WATCHER_STATE = 1
|
||||||
LAYOUT_WATCHER_LAYOUT = 2
|
LAYOUT_WATCHER_LAYOUT = 2
|
||||||
|
|
||||||
|
try:
|
||||||
|
import trezorui2
|
||||||
|
|
||||||
|
UI2 = True
|
||||||
|
except ImportError:
|
||||||
|
UI2 = False
|
||||||
|
|
||||||
def screenshot() -> bool:
|
def screenshot() -> bool:
|
||||||
if storage.save_screen:
|
if storage.save_screen:
|
||||||
display.save(storage.save_screen_directory + "/refresh-")
|
display.save(storage.save_screen_directory + "/refresh-")
|
||||||
@ -62,10 +69,20 @@ if __debug__:
|
|||||||
layout_change_chan.publish(storage.current_content)
|
layout_change_chan.publish(storage.current_content)
|
||||||
|
|
||||||
async def dispatch_debuglink_decision(msg: DebugLinkDecision) -> None:
|
async def dispatch_debuglink_decision(msg: DebugLinkDecision) -> None:
|
||||||
from trezor.enums import DebugButton
|
from trezor.enums import DebugButton, DebugSwipeDirection
|
||||||
from trezor.enums import DebugSwipeDirection
|
|
||||||
from trezor.ui import Result
|
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 is not None:
|
||||||
if msg.button == DebugButton.NO:
|
if msg.button == DebugButton.NO:
|
||||||
|
@ -24,11 +24,47 @@ class _RustLayout(ui.Layout):
|
|||||||
def set_timer(self, token: int, deadline: int) -> None:
|
def set_timer(self, token: int, deadline: int) -> None:
|
||||||
self.timer.schedule(deadline, token)
|
self.timer.schedule(deadline, token)
|
||||||
|
|
||||||
def create_tasks(self) -> tuple[loop.Task, ...]:
|
if __debug__:
|
||||||
return self.handle_input_and_rendering(), self.handle_timers()
|
|
||||||
|
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]
|
def handle_input_and_rendering(self) -> loop.Task: # type: ignore [awaitable-is-generator]
|
||||||
touch = loop.wait(io.TOUCH)
|
touch = loop.wait(io.TOUCH)
|
||||||
|
self._before_render()
|
||||||
ui.display.clear()
|
ui.display.clear()
|
||||||
self.layout.paint()
|
self.layout.paint()
|
||||||
# self.layout.bounds()
|
# self.layout.bounds()
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import hashlib
|
import hashlib
|
||||||
import json
|
import json
|
||||||
|
import os
|
||||||
import re
|
import re
|
||||||
import shutil
|
import shutil
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
@ -112,6 +113,9 @@ def screen_recording(
|
|||||||
# Making the model global for other functions
|
# Making the model global for other functions
|
||||||
global MODEL
|
global MODEL
|
||||||
MODEL = f"T{client.features.model}"
|
MODEL = f"T{client.features.model}"
|
||||||
|
if os.getenv("UI2") == "1":
|
||||||
|
MODEL += "ui2"
|
||||||
|
|
||||||
test_name = f"{MODEL}_{test_name}"
|
test_name = f"{MODEL}_{test_name}"
|
||||||
|
|
||||||
screens_test_path = SCREENS_DIR / test_name
|
screens_test_path = SCREENS_DIR / test_name
|
||||||
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user