use client_application::{ClientApp, RepaintCommand}; use client_domain::BoundingBox; use protocol::{ ServerMessage, WidgetDescriptor, WireDisplayHint, WireLayoutNode, WireContainerNode, WireLayoutChild, WireDirection, WireSizing, WireWidgetState, WireKeyValue, WireValue, }; fn screen() -> BoundingBox { BoundingBox::screen(240, 320) } fn weather_descriptor(id: u16, temp: &str) -> WidgetDescriptor { WidgetDescriptor { id, display_hint: WireDisplayHint::IconValue, state: WireWidgetState { data: vec![ WireKeyValue { key: "temperature".into(), value: WireValue::String(temp.into()) }, ], error: None, }, } } fn two_widget_layout() -> WireLayoutNode { WireLayoutNode::Container(WireContainerNode { direction: WireDirection::Row, gap: 0, padding: 0, children: vec![ WireLayoutChild { sizing: WireSizing::Flex(1), node: WireLayoutNode::Leaf(1) }, WireLayoutChild { sizing: WireSizing::Flex(1), node: WireLayoutNode::Leaf(2) }, ], }) } #[test] fn screen_update_repaints_all_widgets() { let mut app = ClientApp::new(screen()); let msg = ServerMessage::ScreenUpdate { layout: two_widget_layout(), widgets: vec![ weather_descriptor(1, "5.4°C"), weather_descriptor(2, "20°C"), ], }; let repaints = app.handle_message(msg); assert_eq!(repaints.len(), 2); assert_eq!(repaints[0].widget_id, 1); assert_eq!(repaints[0].bounds, BoundingBox::new(0, 0, 120, 320)); assert_eq!(repaints[1].widget_id, 2); assert_eq!(repaints[1].bounds, BoundingBox::new(120, 0, 120, 320)); } #[test] fn data_update_only_repaints_changed_widgets() { let mut app = ClientApp::new(screen()); app.handle_message(ServerMessage::ScreenUpdate { layout: two_widget_layout(), widgets: vec![ weather_descriptor(1, "5.4°C"), weather_descriptor(2, "20°C"), ], }); let repaints = app.handle_message(ServerMessage::DataUpdate { widgets: vec![weather_descriptor(1, "6.1°C")], }); assert_eq!(repaints.len(), 1); assert_eq!(repaints[0].widget_id, 1); assert_eq!( repaints[0].state.data[0].value, WireValue::String("6.1°C".into()) ); } #[test] fn data_update_with_unchanged_data_produces_no_repaints() { let mut app = ClientApp::new(screen()); app.handle_message(ServerMessage::ScreenUpdate { layout: two_widget_layout(), widgets: vec![ weather_descriptor(1, "5.4°C"), weather_descriptor(2, "20°C"), ], }); let repaints = app.handle_message(ServerMessage::DataUpdate { widgets: vec![weather_descriptor(1, "5.4°C")], }); assert!(repaints.is_empty()); } #[test] fn second_screen_update_repaints_all_widgets_with_new_layout() { let mut app = ClientApp::new(screen()); app.handle_message(ServerMessage::ScreenUpdate { layout: two_widget_layout(), widgets: vec![ weather_descriptor(1, "5.4°C"), weather_descriptor(2, "20°C"), ], }); let column_layout = WireLayoutNode::Container(WireContainerNode { direction: WireDirection::Column, gap: 0, padding: 0, children: vec![ WireLayoutChild { sizing: WireSizing::Flex(1), node: WireLayoutNode::Leaf(1) }, WireLayoutChild { sizing: WireSizing::Flex(1), node: WireLayoutNode::Leaf(2) }, ], }); let repaints = app.handle_message(ServerMessage::ScreenUpdate { layout: column_layout, widgets: vec![ weather_descriptor(1, "5.4°C"), weather_descriptor(2, "20°C"), ], }); assert_eq!(repaints.len(), 2); assert_eq!(repaints[0].bounds, BoundingBox::new(0, 0, 240, 160)); assert_eq!(repaints[1].bounds, BoundingBox::new(0, 160, 240, 160)); } #[test] fn data_update_before_screen_update_produces_no_repaints() { let mut app = ClientApp::new(screen()); let repaints = app.handle_message(ServerMessage::DataUpdate { widgets: vec![weather_descriptor(1, "5.4°C")], }); assert!(repaints.is_empty()); } #[test] fn heartbeat_produces_no_repaints() { let mut app = ClientApp::new(screen()); let repaints = app.handle_message(ServerMessage::Heartbeat); assert!(repaints.is_empty()); }