Add error handling to custom measure functions
1use selenium_rs::element::Element;
2use selenium_rs::webdriver::{Browser, Selector, WebDriver};
3
4use std::process::Command;
5use std::path::Path;
6use std::fs;
7use std::{thread, time};
8
9use json;
10
11fn main() {
12 // std::env::current_exe should be <path/to/repo>/scripts/gentest/target/debug/gentest
13 // move up five times to get to repo root
14 let root_dir = std::env::current_exe().unwrap();
15 let repo_root = root_dir.parent()
16 .and_then(Path::parent)
17 .and_then(Path::parent)
18 .and_then(Path::parent)
19 .and_then(Path::parent)
20 .unwrap();
21
22 let fixtures_root = repo_root.join("test_fixtures");
23
24 let mut selenium = Command::new("java")
25 .arg("-jar")
26 .arg("selenium.jar")
27 .current_dir(&format!("{}/scripts/gentest", repo_root.display()))
28 .spawn()
29 .expect("failed to execute selenium, most likely missing java");
30
31 // Wait for a bit until selenium is up and running...
32 let ten_millis = time::Duration::from_millis(2000);
33 thread::sleep(ten_millis);
34
35 let mut driver = WebDriver::new(Browser::Chrome);
36 driver.start_session();
37
38 let mut src = String::new();
39 src.push_str("#[cfg(test)]\n");
40 src.push_str("mod generated {\n");
41
42 let fixtures = fs::read_dir(fixtures_root).unwrap();
43 for fixture in fixtures {
44 let fixture_path = fixture.unwrap().path();
45 if fixture_path.is_file() {
46 let name = fixture_path.file_stem().unwrap().to_str().unwrap();
47
48 driver.navigate(&format!("file://{}", fixture_path.display()));
49 let root_element = driver.query_element(Selector::CSS, "#test-root").unwrap();
50 src.push_str(&format!("{}\n", generate_test(&name, root_element)));
51 }
52 }
53
54 src.push_str("}\n");
55 fs::write(repo_root.join("tests").join("generated.rs"), src);
56
57 Command::new("cargo")
58 .arg("fmt")
59 .current_dir(repo_root)
60 .status();
61
62 driver.delete_session();
63 selenium.kill();
64}
65
66fn generate_test(name: &str, root: Element) -> String {
67 let description_string = root.get_property("__stretch_description__").unwrap();
68 let description = json::parse(&description_string).unwrap();
69
70 let mut src = String::new();
71 src.push_str("#[test]\n");
72 src.push_str(&format!("fn {}() {{\n", name));
73
74 src.push_str("let layout = stretch::compute(\n");
75 src.push_str(&format!("&{}", &generate_node(&description)));
76 src.push_str(", stretch::geometry::Size::undefined()).unwrap();\n\n");
77
78 src.push_str(&generate_assertions("layout".to_string(), &description));
79 src.push_str("}\n");
80 src
81}
82
83fn generate_assertions(prefix: String, node: &json::JsonValue) -> String {
84 let layout = &node["layout"];
85 let mut src = String::new();
86
87 src.push_str(&format!("assert_eq!({}.size.width, {:.4});\n", prefix, layout["width"].as_f32().unwrap()));
88 src.push_str(&format!("assert_eq!({}.size.height, {:.4});\n", prefix, layout["height"].as_f32().unwrap()));
89 src.push_str(&format!("assert_eq!({}.location.x, {:.4});\n", prefix, layout["x"].as_f32().unwrap()));
90 src.push_str(&format!("assert_eq!({}.location.y, {:.4});\n\n", prefix, layout["y"].as_f32().unwrap()));
91
92 match node["children"] {
93 json::JsonValue::Array(ref value) => {
94 for i in 0..value.len() {
95 let child = &value[i];
96 src.push_str(&generate_assertions(format!("{}.children[{}]", prefix, i), child));
97 }
98 },
99 _ => (),
100 };
101 src
102}
103
104fn generate_node(node: &json::JsonValue) -> String {
105 let mut src = String::new();
106 src.push_str("stretch::style::Node {\n");
107
108 let style = &node["style"];
109
110 match style["display"] {
111 json::JsonValue::Short(ref value) => {
112 match value.as_ref() {
113 "none" => src.push_str("display: stretch::style::Display::None,\n"),
114 _ => (),
115 };
116 },
117 _ => (),
118 };
119
120 match style["position_type"] {
121 json::JsonValue::Short(ref value) => {
122 match value.as_ref() {
123 "absolute" => src.push_str("position_type: stretch::style::PositionType::Absolute,\n"),
124 _ => (),
125 };
126 },
127 _ => (),
128 };
129
130 match style["direction"] {
131 json::JsonValue::Short(ref value) => {
132 match value.as_ref() {
133 "rtl" => src.push_str("direction: stretch::style::Direction::RTL,\n"),
134 "ltr" => src.push_str("direction: stretch::style::Direction::LTR,\n"),
135 _ => (),
136 };
137 },
138 _ => (),
139 };
140
141 match style["flexDirection"] {
142 json::JsonValue::Short(ref value) => {
143 match value.as_ref() {
144 "row-reverse" => src.push_str("flex_direction: stretch::style::FlexDirection::RowReverse,\n"),
145 "column" => src.push_str("flex_direction: stretch::style::FlexDirection::Column,\n"),
146 "column-reverse" => src.push_str("flex_direction: stretch::style::FlexDirection::ColumnReverse,\n"),
147 _ => (),
148 };
149 },
150 _ => (),
151 };
152
153 match style["flexWrap"] {
154 json::JsonValue::Short(ref value) => {
155 match value.as_ref() {
156 "wrap" => src.push_str("flex_wrap: stretch::style::FlexWrap::Wrap,\n"),
157 "wrap-reverse" => src.push_str("flex_wrap: stretch::style::FlexWrap::WrapReverse,\n"),
158 _ => (),
159 };
160 },
161 _ => (),
162 };
163
164 match style["overflow"] {
165 json::JsonValue::Short(ref value) => {
166 match value.as_ref() {
167 "hidden" => src.push_str("overflow: stretch::style::Overflow::Hidden,\n"),
168 "scroll" => src.push_str("overflow: stretch::style::Overflow::Scroll,\n"),
169 _ => (),
170 };
171 },
172 _ => (),
173 };
174
175 match style["alignItems"] {
176 json::JsonValue::Short(ref value) => {
177 match value.as_ref() {
178 "flex-start" => src.push_str("align_items: stretch::style::AlignItems::FlexStart,\n"),
179 "flex-end" => src.push_str("align_items: stretch::style::AlignItems::FlexEnd,\n"),
180 "center" => src.push_str("align_items: stretch::style::AlignItems::Center,\n"),
181 "baseline" => src.push_str("align_items: stretch::style::AlignItems::Baseline,\n"),
182 _ => (),
183 };
184 },
185 _ => (),
186 };
187
188 match style["alignSelf"] {
189 json::JsonValue::Short(ref value) => {
190 match value.as_ref() {
191 "flex-start" => src.push_str("align_self: stretch::style::AlignSelf::FlexStart,\n"),
192 "flex-end" => src.push_str("align_self: stretch::style::AlignSelf::FlexEnd,\n"),
193 "center" => src.push_str("align_self: stretch::style::AlignSelf::Center,\n"),
194 "baseline" => src.push_str("align_self: stretch::style::AlignSelf::Baseline,\n"),
195 "stretch" => src.push_str("align_self: stretch::style::AlignSelf::Stretch,\n"),
196 _ => (),
197 };
198 },
199 _ => (),
200 };
201
202 match style["alignContent"] {
203 json::JsonValue::Short(ref value) => {
204 match value.as_ref() {
205 "flex-start" => src.push_str("align_content: stretch::style::AlignContent::FlexStart,\n"),
206 "flex-end" => src.push_str("align_content: stretch::style::AlignContent::FlexEnd,\n"),
207 "center" => src.push_str("align_content: stretch::style::AlignContent::Center,\n"),
208 "space-between" => src.push_str("align_content: stretch::style::AlignContent::SpaceBetween,\n"),
209 "space-around" => src.push_str("align_content: stretch::style::AlignContent::SpaceAround,\n"),
210 _ => (),
211 };
212 },
213 _ => (),
214 };
215
216 match style["justifyContent"] {
217 json::JsonValue::Short(ref value) => {
218 match value.as_ref() {
219 "flex-end" => src.push_str("justify_content: stretch::style::JustifyContent::FlexEnd,\n"),
220 "center" => src.push_str("justify_content: stretch::style::JustifyContent::Center,\n"),
221 "space-between" => src.push_str("justify_content: stretch::style::JustifyContent::SpaceBetween,\n"),
222 "space-around" => src.push_str("justify_content: stretch::style::JustifyContent::SpaceAround,\n"),
223 "space-evenly" => src.push_str("justify_content: stretch::style::JustifyContent::SpaceEvenly,\n"),
224 _ => (),
225 };
226 },
227 _ => (),
228 };
229
230 match style["flexGrow"] {
231 json::JsonValue::Number(value) => {
232 let value: f32 = value.into();
233 src.push_str(&format!("flex_grow: {:.4},\n", value))
234 },
235 _ => (),
236 };
237
238 match style["flexShrink"] {
239 json::JsonValue::Number(value) => {
240 let value: f32 = value.into();
241 src.push_str(&format!("flex_shrink: {:.4},\n", value))
242 },
243 _ => (),
244 };
245
246 match style["flexBasis"] {
247 json::JsonValue::Object(ref value) => src.push_str(&format!("flex_basis: {},\n", generate_dimension(value))),
248 _ => (),
249 };
250
251 match style["size"] {
252 json::JsonValue::Object(ref value) => src.push_str(&format!("size: {},\n", generate_size(value))),
253 _ => (),
254 };
255
256 match style["min_size"] {
257 json::JsonValue::Object(ref value) => src.push_str(&format!("min_size: {},\n", generate_size(value))),
258 _ => (),
259 };
260
261 match style["max_size"] {
262 json::JsonValue::Object(ref value) => src.push_str(&format!("max_size: {},\n", generate_size(value))),
263 _ => (),
264 };
265
266 match style["margin"] {
267 json::JsonValue::Object(ref value) => src.push_str(&format!("margin: {},\n", generate_edges(value))),
268 _ => (),
269 };
270
271 match style["padding"] {
272 json::JsonValue::Object(ref value) => src.push_str(&format!("padding: {},\n", generate_edges(value))),
273 _ => (),
274 };
275
276 match style["position"] {
277 json::JsonValue::Object(ref value) => src.push_str(&format!("position: {},\n", generate_edges(value))),
278 _ => (),
279 };
280
281 match style["border"] {
282 json::JsonValue::Object(ref value) => src.push_str(&format!("border: {},\n", generate_edges(value))),
283 _ => (),
284 };
285
286 match node["children"] {
287 json::JsonValue::Array(ref value) => {
288 if value.len() > 0 {
289 src.push_str("children: vec![\n");
290 value.iter().for_each(|child| {
291 src.push_str(&format!("{},\n", generate_node(child)));
292 });
293 src.push_str("],\n");
294 }
295 },
296 _ => (),
297 };
298
299 src.push_str("..Default::default()\n");
300 src.push_str("}\n");
301 src
302}
303
304fn generate_size(size: &json::object::Object) -> String {
305 let mut src = String::new();
306 src.push_str("stretch::geometry::Size {\n");
307
308 match size.get("width") {
309 Some(value) => match value {
310 json::JsonValue::Object(ref value) => src.push_str(&format!("width: {},\n", generate_dimension(value))),
311 _ => (),
312 },
313 None => (),
314 };
315
316 match size.get("height") {
317 Some(value) => match value {
318 json::JsonValue::Object(ref value) => src.push_str(&format!("height: {},\n", generate_dimension(value))),
319 _ => (),
320 },
321 None => (),
322 };
323
324 src.push_str("..Default::default()\n");
325 src.push_str("}\n");
326 src
327}
328
329fn generate_dimension(dimen: &json::object::Object) -> String {
330 let unit = dimen.get("unit").unwrap();
331 let value = || dimen.get("value").unwrap().as_f32().unwrap();
332
333 match unit {
334 json::JsonValue::Short(ref unit) => {
335 match unit.as_ref() {
336 "auto" => format!("stretch::style::Dimension::Auto"),
337 "points" => format!("stretch::style::Dimension::Points({:.4})", value()),
338 "percent" => format!("stretch::style::Dimension::Percent({:.4})", value()),
339 _ => panic!(),
340 }
341 },
342 _ => panic!(),
343 }
344}
345
346fn generate_edges(dimen: &json::object::Object) -> String {
347 let mut src = String::new();
348 src.push_str("stretch::geometry::Rect {\n");
349
350 match dimen.get("start") {
351 Some(value) => match value {
352 json::JsonValue::Object(ref value) => src.push_str(&format!("start: {},\n", generate_dimension(value))),
353 _ => (),
354 },
355 None => (),
356 };
357
358 match dimen.get("end") {
359 Some(value) => match value {
360 json::JsonValue::Object(ref value) => src.push_str(&format!("end: {},\n", generate_dimension(value))),
361 _ => (),
362 },
363 None => (),
364 };
365
366 match dimen.get("top") {
367 Some(value) => match value {
368 json::JsonValue::Object(ref value) => src.push_str(&format!("top: {},\n", generate_dimension(value))),
369 _ => (),
370 },
371 None => (),
372 };
373
374 match dimen.get("bottom") {
375 Some(value) => match value {
376 json::JsonValue::Object(ref value) => src.push_str(&format!("bottom: {},\n", generate_dimension(value))),
377 _ => (),
378 },
379 None => (),
380 };
381
382 src.push_str("..Default::default()\n");
383 src.push_str("}\n");
384 src
385}