From 80655747d00db853c92855c6d70d223331a985a9 Mon Sep 17 00:00:00 2001 From: Martin Milata Date: Mon, 7 Feb 2022 21:36:26 +0100 Subject: [PATCH] test(core/rust/ui): pagination unit test [no changelog] --- core/embed/rust/src/trace.rs | 40 +++++ core/embed/rust/src/ui/component/empty.rs | 8 + .../rust/src/ui/component/text/paragraphs.rs | 64 +++---- core/embed/rust/src/ui/model_t1/layout.rs | 35 ---- .../rust/src/ui/model_tt/component/page.rs | 168 ++++++++++++++++++ core/embed/rust/src/ui/model_tt/layout.rs | 37 +--- 6 files changed, 238 insertions(+), 114 deletions(-) diff --git a/core/embed/rust/src/trace.rs b/core/embed/rust/src/trace.rs index fd580a337..6af52b82c 100644 --- a/core/embed/rust/src/trace.rs +++ b/core/embed/rust/src/trace.rs @@ -49,3 +49,43 @@ where } } } + +#[cfg(test)] +mod tests { + use super::*; + + impl Tracer for Vec { + fn int(&mut self, i: i64) { + self.string(&i.to_string()); + } + + fn bytes(&mut self, b: &[u8]) { + self.extend(b) + } + + fn string(&mut self, s: &str) { + self.extend(s.as_bytes()) + } + + fn symbol(&mut self, name: &str) { + self.extend(name.as_bytes()) + } + + fn open(&mut self, name: &str) { + self.extend(b"<"); + self.extend(name.as_bytes()); + self.extend(b" "); + } + + fn field(&mut self, name: &str, value: &dyn Trace) { + self.extend(name.as_bytes()); + self.extend(b":"); + value.trace(self); + self.extend(b" "); + } + + fn close(&mut self) { + self.extend(b">") + } + } +} diff --git a/core/embed/rust/src/ui/component/empty.rs b/core/embed/rust/src/ui/component/empty.rs index ee13a184c..f803ce32b 100644 --- a/core/embed/rust/src/ui/component/empty.rs +++ b/core/embed/rust/src/ui/component/empty.rs @@ -11,3 +11,11 @@ impl Component for Empty { fn paint(&mut self) {} } + +#[cfg(feature = "ui_debug")] +impl crate::trace::Trace for Empty { + fn trace(&self, t: &mut dyn crate::trace::Tracer) { + t.open("Empty"); + t.close(); + } +} diff --git a/core/embed/rust/src/ui/component/text/paragraphs.rs b/core/embed/rust/src/ui/component/text/paragraphs.rs index 58ff49a12..86a070cb7 100644 --- a/core/embed/rust/src/ui/component/text/paragraphs.rs +++ b/core/embed/rust/src/ui/component/text/paragraphs.rs @@ -150,16 +150,28 @@ impl Dimensions for Paragraphs { } #[cfg(feature = "ui_debug")] -impl crate::trace::Trace for Paragraphs -where - T: AsRef<[u8]>, -{ - fn trace(&self, t: &mut dyn crate::trace::Tracer) { - t.open("Paragraphs"); - for paragraph in &self.list { - paragraph.trace(t); +pub mod trace { + use super::*; + use crate::ui::component::text::layout::trace::TraceSink; + + impl crate::trace::Trace for Paragraphs + where + T: AsRef<[u8]>, + { + fn trace(&self, t: &mut dyn crate::trace::Tracer) { + t.open("Paragraphs"); + let mut char_offset = self.offset.chr; + for paragraph in self.list.iter().skip(self.offset.par).take(self.visible) { + paragraph.layout.layout_text( + ¶graph.content.as_ref()[char_offset..], + &mut paragraph.layout.initial_cursor(), + &mut TraceSink(t), + ); + t.string("\n"); + char_offset = 0; + } + t.close(); } - t.close(); } } @@ -292,37 +304,3 @@ where } } } - -#[cfg(feature = "ui_debug")] -pub mod trace { - use crate::ui::component::text::layout::trace::TraceSink; - - use super::*; - - pub struct TraceText<'a, T>(pub &'a Paragraph); - - impl<'a, T> crate::trace::Trace for TraceText<'a, T> - where - T: AsRef<[u8]>, - { - fn trace(&self, d: &mut dyn crate::trace::Tracer) { - self.0.layout.layout_text( - self.0.content.as_ref(), - &mut self.0.layout.initial_cursor(), - &mut TraceSink(d), - ); - } - } -} - -#[cfg(feature = "ui_debug")] -impl crate::trace::Trace for Paragraph -where - T: AsRef<[u8]>, -{ - fn trace(&self, t: &mut dyn crate::trace::Tracer) { - t.open("Paragraph"); - t.field("content", &trace::TraceText(self)); - t.close(); - } -} diff --git a/core/embed/rust/src/ui/model_t1/layout.rs b/core/embed/rust/src/ui/model_t1/layout.rs index 60e17178c..3c3d353af 100644 --- a/core/embed/rust/src/ui/model_t1/layout.rs +++ b/core/embed/rust/src/ui/model_t1/layout.rs @@ -101,41 +101,6 @@ mod tests { use super::*; - impl Tracer for Vec { - fn int(&mut self, i: i64) { - self.string(&i.to_string()); - } - - fn bytes(&mut self, b: &[u8]) { - self.extend(b) - } - - fn string(&mut self, s: &str) { - self.extend(s.as_bytes()) - } - - fn symbol(&mut self, name: &str) { - self.extend(name.as_bytes()) - } - - fn open(&mut self, name: &str) { - self.extend(b"<"); - self.extend(name.as_bytes()); - self.extend(b" "); - } - - fn field(&mut self, name: &str, value: &dyn Trace) { - self.extend(name.as_bytes()); - self.extend(b":"); - value.trace(self); - self.extend(b" "); - } - - fn close(&mut self) { - self.extend(b">") - } - } - fn trace(val: &impl Trace) -> String { let mut t = Vec::new(); val.trace(&mut t); diff --git a/core/embed/rust/src/ui/model_tt/component/page.rs b/core/embed/rust/src/ui/model_tt/component/page.rs index aedc50893..f10bc9bea 100644 --- a/core/embed/rust/src/ui/model_tt/component/page.rs +++ b/core/embed/rust/src/ui/model_tt/component/page.rs @@ -170,6 +170,7 @@ where t.field("active_page", &self.scrollbar.active_page); t.field("page_count", &self.scrollbar.page_count); t.field("content", &self.content); + t.field("buttons", &self.buttons); t.close(); } } @@ -307,3 +308,170 @@ impl PageLayout { } } } + +#[cfg(test)] +mod tests { + use crate::{ + trace::Trace, + ui::{ + component::{text::paragraphs::Paragraphs, Empty}, + display, + geometry::Point, + model_tt::{event::TouchEvent, theme}, + }, + }; + + use super::*; + + fn trace(val: &impl Trace) -> String { + let mut t = Vec::new(); + val.trace(&mut t); + String::from_utf8(t).unwrap() + } + + fn swipe(component: &mut impl Component, points: &[(i32, i32)]) { + let last = points.len().saturating_sub(1); + let mut first = true; + let mut ctx = EventCtx::new(); + for (i, &(x, y)) in points.iter().enumerate() { + let p = Point::new(x, y); + let ev = if first { + TouchEvent::TouchStart(p) + } else if i == last { + TouchEvent::TouchEnd(p) + } else { + TouchEvent::TouchMove(p) + }; + component.event(&mut ctx, Event::Touch(ev)); + first = false; + } + } + + fn swipe_up(component: &mut impl Component) { + swipe(component, &[(20, 100), (20, 60), (20, 20)]) + } + + fn swipe_down(component: &mut impl Component) { + swipe(component, &[(20, 20), (20, 60), (20, 100)]) + } + + #[test] + fn paragraphs_empty() { + let mut page = SwipePage::new( + display::screen(), + theme::BG, + |area| Paragraphs::<&[u8]>::new(area), + |_| Empty, + ); + + let expected = + " buttons: >"; + + assert_eq!(trace(&page), expected); + swipe_up(&mut page); + assert_eq!(trace(&page), expected); + swipe_down(&mut page); + assert_eq!(trace(&page), expected); + } + + #[test] + fn paragraphs_single() { + let mut page = SwipePage::new( + display::screen(), + theme::BG, + |area| { + Paragraphs::new(area) + .add::( + theme::FONT_NORMAL, + "This is the first paragraph and it should fit on the screen entirely.", + ) + .add::( + theme::FONT_BOLD, + "Second, bold, paragraph should also fit on the screen whole I think.", + ) + }, + |_| Empty, + ); + + let expected = " buttons: >"; + + assert_eq!(trace(&page), expected); + swipe_up(&mut page); + assert_eq!(trace(&page), expected); + swipe_down(&mut page); + assert_eq!(trace(&page), expected); + } + + #[test] + fn paragraphs_one_long() { + let mut page = SwipePage::new( + display::screen(), + theme::BG, + |area| { + Paragraphs::new(area) + .add::( + theme::FONT_BOLD, + "This is somewhat long paragraph that goes on and on and on and on and on and will definitely not fit on just a single screen. You have to swipe a bit to see all the text it contains I guess. There's just so much letters in it.", + ) + }, + |_| Empty, + ); + + let expected1 = " buttons: >"; + let expected2 = " buttons: >"; + + assert_eq!(trace(&page), expected1); + swipe_down(&mut page); + assert_eq!(trace(&page), expected1); + swipe_up(&mut page); + assert_eq!(trace(&page), expected2); + swipe_up(&mut page); + assert_eq!(trace(&page), expected2); + swipe_down(&mut page); + assert_eq!(trace(&page), expected1); + } + + #[test] + fn paragraphs_three_long() { + let mut page = SwipePage::new( + display::screen(), + theme::BG, + |area| { + Paragraphs::new(area) + .add::( + theme::FONT_BOLD, + "This paragraph is using a bold font. It doesn't need to be all that long.", + ) + .add::( + theme::FONT_MONO, + "And this one is using MONO. Monospace is nice for numbers, they have the same width and can be scanned quickly. Even if they span several pages or something.", + ) + .add::( + theme::FONT_BOLD, + "Let's add another one for a good measure. This one should overflow all the way to the third page with a bit of luck.", + ) + }, + |_| Empty, + ); + + let expected1 = " buttons: >"; + let expected2 = " buttons: >"; + let expected3 = " buttons: >"; + + assert_eq!(trace(&page), expected1); + swipe_down(&mut page); + assert_eq!(trace(&page), expected1); + swipe_up(&mut page); + assert_eq!(trace(&page), expected2); + swipe_up(&mut page); + assert_eq!(trace(&page), expected3); + swipe_up(&mut page); + assert_eq!(trace(&page), expected3); + swipe_down(&mut page); + assert_eq!(trace(&page), expected2); + swipe_down(&mut page); + assert_eq!(trace(&page), expected1); + swipe_down(&mut page); + assert_eq!(trace(&page), expected1); + } +} diff --git a/core/embed/rust/src/ui/model_tt/layout.rs b/core/embed/rust/src/ui/model_tt/layout.rs index 3a6bdf840..3af5dbafc 100644 --- a/core/embed/rust/src/ui/model_tt/layout.rs +++ b/core/embed/rust/src/ui/model_tt/layout.rs @@ -122,47 +122,12 @@ extern "C" fn ui_layout_new_confirm_action( #[cfg(test)] mod tests { use crate::{ - trace::{Trace, Tracer}, + trace::Trace, ui::model_tt::component::{Button, Dialog}, }; use super::*; - impl Tracer for Vec { - fn int(&mut self, i: i64) { - self.string(&i.to_string()); - } - - fn bytes(&mut self, b: &[u8]) { - self.extend(b); - } - - fn string(&mut self, s: &str) { - self.extend(s.as_bytes()); - } - - fn symbol(&mut self, name: &str) { - self.extend(name.as_bytes()); - } - - fn open(&mut self, name: &str) { - self.extend(b"<"); - self.extend(name.as_bytes()); - self.extend(b" "); - } - - fn field(&mut self, name: &str, value: &dyn Trace) { - self.extend(name.as_bytes()); - self.extend(b":"); - value.trace(self); - self.extend(b" "); - } - - fn close(&mut self) { - self.extend(b">"); - } - } - fn trace(val: &impl Trace) -> String { let mut t = Vec::new(); val.trace(&mut t);