Server: domain (entities, value objects, ports), protocol (postcard wire types), application (config service, data projection), adapters (config-memory, tcp-server), bootstrap (composition root with fake data). Client: client-domain (layout engine, render tree, HAL ports), client-application (message handling, repaint commands), adapters (tcp-client, display-terminal), client-desktop (end-to-end working). ESP32: client-esp32 firmware with ILI9341 display over SPI, WiFi networking. Display test verified on hardware — landscape orientation, text rendering works. 60 workspace tests, all passing.
86 lines
2.5 KiB
Rust
86 lines
2.5 KiB
Rust
use domain::{
|
|
LayoutNode, ContainerNode, LayoutChild, Direction, Sizing,
|
|
};
|
|
use client_domain::{BoundingBox, LayoutEngine};
|
|
|
|
fn screen() -> BoundingBox {
|
|
BoundingBox::screen(240, 320)
|
|
}
|
|
|
|
#[test]
|
|
fn diff_detects_moved_widget_after_layout_change() {
|
|
let layout_a = LayoutNode::Container(ContainerNode {
|
|
direction: Direction::Row,
|
|
gap: 0,
|
|
padding: 0,
|
|
children: vec![
|
|
LayoutChild { sizing: Sizing::Flex(1), node: LayoutNode::Leaf(1) },
|
|
LayoutChild { sizing: Sizing::Flex(1), node: LayoutNode::Leaf(2) },
|
|
],
|
|
});
|
|
|
|
let layout_b = LayoutNode::Container(ContainerNode {
|
|
direction: Direction::Column,
|
|
gap: 0,
|
|
padding: 0,
|
|
children: vec![
|
|
LayoutChild { sizing: Sizing::Flex(1), node: LayoutNode::Leaf(1) },
|
|
LayoutChild { sizing: Sizing::Flex(1), node: LayoutNode::Leaf(2) },
|
|
],
|
|
});
|
|
|
|
let tree_a = LayoutEngine::compute(&layout_a, screen());
|
|
let tree_b = LayoutEngine::compute(&layout_b, screen());
|
|
|
|
let mut changed = tree_b.diff(&tree_a);
|
|
changed.sort();
|
|
assert_eq!(changed, vec![1, 2]);
|
|
}
|
|
|
|
#[test]
|
|
fn diff_returns_empty_for_identical_layouts() {
|
|
let layout = LayoutNode::Container(ContainerNode {
|
|
direction: Direction::Row,
|
|
gap: 0,
|
|
padding: 0,
|
|
children: vec![
|
|
LayoutChild { sizing: Sizing::Flex(1), node: LayoutNode::Leaf(1) },
|
|
LayoutChild { sizing: Sizing::Flex(1), node: LayoutNode::Leaf(2) },
|
|
],
|
|
});
|
|
|
|
let tree_a = LayoutEngine::compute(&layout, screen());
|
|
let tree_b = LayoutEngine::compute(&layout, screen());
|
|
|
|
assert!(tree_b.diff(&tree_a).is_empty());
|
|
}
|
|
|
|
#[test]
|
|
fn diff_detects_added_and_removed_widgets() {
|
|
let layout_a = LayoutNode::Container(ContainerNode {
|
|
direction: Direction::Row,
|
|
gap: 0,
|
|
padding: 0,
|
|
children: vec![
|
|
LayoutChild { sizing: Sizing::Flex(1), node: LayoutNode::Leaf(1) },
|
|
],
|
|
});
|
|
|
|
let layout_b = LayoutNode::Container(ContainerNode {
|
|
direction: Direction::Row,
|
|
gap: 0,
|
|
padding: 0,
|
|
children: vec![
|
|
LayoutChild { sizing: Sizing::Flex(1), node: LayoutNode::Leaf(2) },
|
|
],
|
|
});
|
|
|
|
let tree_a = LayoutEngine::compute(&layout_a, screen());
|
|
let tree_b = LayoutEngine::compute(&layout_b, screen());
|
|
|
|
let mut changed = tree_b.diff(&tree_a);
|
|
changed.sort();
|
|
// widget 1 was removed (in old but not new), widget 2 was added (in new but not old)
|
|
assert_eq!(changed, vec![1, 2]);
|
|
}
|