add all crates: domain, protocol, application, client, adapters, ESP32 firmware
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.
This commit is contained in:
85
crates/client-domain/tests/render_tree_tests.rs
Normal file
85
crates/client-domain/tests/render_tree_tests.rs
Normal file
@@ -0,0 +1,85 @@
|
||||
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]);
|
||||
}
|
||||
Reference in New Issue
Block a user