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