1use sim_kernel::Expr;
33
34use crate::codec::reduce_for_caps;
35use crate::surface;
36
37pub const DEVICE_PRESETS: &[&str] = &["watch", "glasses", "phone", "desktop"];
39
40pub fn project_for_preset(scene: &Expr, preset_name: &str) -> Option<Expr> {
46 let caps = surface::preset(preset_name)?;
47 Some(reduce_for_caps(scene, &caps))
48}
49
50#[cfg(test)]
51mod tests {
52 use super::*;
53
54 fn rich_scene() -> Expr {
55 sim_lib_scene::build::stack(
56 "column",
57 vec![
58 sim_lib_scene::build::text_node("one"),
59 sim_lib_scene::build::text_node("two"),
60 sim_lib_scene::build::text_node("three"),
61 sim_lib_scene::build::text_node("four"),
62 sim_lib_scene::build::text_node("five"),
63 ],
64 )
65 }
66
67 fn child_count(scene: &Expr) -> usize {
68 let Expr::Map(entries) = scene else {
69 return 0;
70 };
71 for (key, value) in entries {
72 match (key, value) {
73 (Expr::Symbol(symbol), Expr::List(items)) if &*symbol.name == "children" => {
74 return items.len();
75 }
76 _ => {}
77 }
78 }
79 0
80 }
81
82 #[test]
83 fn each_device_class_projects_deterministically_and_validly() {
84 let scene = rich_scene();
85 for preset in DEVICE_PRESETS {
86 let first = project_for_preset(&scene, preset).unwrap();
87 let second = project_for_preset(&scene, preset).unwrap();
88 assert_eq!(first, second, "{preset} projection must be deterministic");
89 assert!(sim_lib_scene::validate_scene(&first).is_ok());
90 }
91 }
92
93 #[test]
94 fn glance_classes_reduce_harder_than_dense() {
95 let scene = rich_scene();
96 assert_eq!(
99 child_count(&project_for_preset(&scene, "watch").unwrap()),
100 1
101 );
102 assert_eq!(
103 child_count(&project_for_preset(&scene, "glasses").unwrap()),
104 1
105 );
106 assert_eq!(
107 child_count(&project_for_preset(&scene, "phone").unwrap()),
108 3
109 );
110 assert_eq!(
111 child_count(&project_for_preset(&scene, "desktop").unwrap()),
112 5
113 );
114 }
115
116 #[test]
117 fn unknown_preset_projects_to_none() {
118 assert!(project_for_preset(&rich_scene(), "hologram").is_none());
119 }
120
121 #[test]
122 fn device_presets_carry_distinguishing_capabilities() {
123 let watch = surface::preset("watch").unwrap();
124 assert!(watch.input_flag("haptic-ack"));
125 assert_eq!(watch.display_density().unwrap().name.as_ref(), "glance");
126
127 let glasses = surface::preset("glasses").unwrap();
128 assert!(glasses.input_flag("voice"));
129 assert_eq!(glasses.display_density().unwrap().name.as_ref(), "glance");
130
131 let phone = surface::preset("phone").unwrap();
132 assert!(phone.input_flag("camera"));
133
134 let desktop = surface::preset("desktop").unwrap();
135 assert!(desktop.input_flag("file-drop"));
136 assert_eq!(desktop.display_density().unwrap().name.as_ref(), "dense");
137 }
138}