-
Notifications
You must be signed in to change notification settings - Fork 155
scrollable_overflow implementation #875
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -232,7 +232,9 @@ fn generate_test(name: impl AsRef<str>, description: &Value) -> TokenStream { | |
| taffy.print_tree(node); | ||
| println!(); | ||
|
|
||
| let mut mismatches = 0u32; | ||
| #assertions | ||
| assert!(mismatches == 0, "Detected {mismatches} mismatch(es)"); | ||
| } | ||
| ) | ||
| } | ||
|
|
@@ -270,44 +272,50 @@ fn generate_assertions(ident: &str, node: &Value, use_rounding: bool) -> TokenSt | |
|
|
||
| let ident = Ident::new(ident, Span::call_site()); | ||
|
|
||
| let gen_check = |expr: TokenStream, expected: f32| { | ||
| let cmp = if use_rounding { | ||
| quote!(layout.#expr != #expected) | ||
| } else { | ||
| quote!((layout.#expr - #expected).abs() >= 0.1) | ||
| }; | ||
| quote!( | ||
| if #cmp { | ||
| mismatches += 1; | ||
| eprintln!("{:?}.{} mismatch: expected {} actual {}", #ident, stringify!(#expr), #expected, layout.#expr); | ||
| } | ||
| ) | ||
| }; | ||
|
|
||
| // The scrollWidth reading from chrome is only accurate if the node is scroll container. So only assert in that case. | ||
| // TODO: accurately test content size in the non-scroll-container case. | ||
| let scroll_assertions = if is_scroll_container { | ||
| let scroll_assertions = if is_scroll_container || true { | ||
| let check_scroll_width = gen_check(quote!(scroll_width()), scroll_width); | ||
| let check_scroll_height = gen_check(quote!(scroll_height()), scroll_height); | ||
| quote!( | ||
| #[cfg(feature = "content_size")] | ||
| assert_eq!(layout.scroll_width(), #scroll_width, "scroll_width of node {:?}. Expected {}. Actual {}", #ident, #scroll_width, layout.scroll_width()); | ||
| #check_scroll_width | ||
| #[cfg(feature = "content_size")] | ||
| assert_eq!(layout.scroll_height(), #scroll_height, "scroll_height of node {:?}. Expected {}. Actual {}", #ident, #scroll_height, layout.scroll_height()); | ||
| #check_scroll_height | ||
| ) | ||
| } else { | ||
| quote!() | ||
| }; | ||
|
|
||
| if use_rounding { | ||
| quote!( | ||
| let layout = taffy.layout(#ident).unwrap(); | ||
| let Layout { size, location, .. } = layout; | ||
| assert_eq!(size.width, #width, "width of node {:?}. Expected {}. Actual {}", #ident, #width, size.width); | ||
| assert_eq!(size.height, #height, "height of node {:?}. Expected {}. Actual {}", #ident, #height, size.height); | ||
| assert_eq!(location.x, #x, "x of node {:?}. Expected {}. Actual {}", #ident, #x, location.x); | ||
| assert_eq!(location.y, #y, "y of node {:?}. Expected {}. Actual {}", #ident, #y, location.y); | ||
| #scroll_assertions | ||
|
|
||
| #children | ||
| ) | ||
| } else { | ||
| quote!( | ||
| let layout = taffy.layout(#ident).unwrap(); | ||
| let Layout { size, location, .. } = layout; | ||
| assert!((size.width - #width).abs() < 0.1, "width of node {:?}. Expected {}. Actual {}", #ident, #width, size.width); | ||
| assert!((size.height - #height).abs() < 0.1, "height of node {:?}. Expected {}. Actual {}", #ident, #height, size.height); | ||
| assert!((location.x - #x).abs() < 0.1, "x of node {:?}. Expected {}. Actual {}", #ident, #x, location.x); | ||
| assert!((location.y - #y).abs() < 0.1, "y of node {:?}. Expected {}. Actual {}", #ident, #y, location.y); | ||
| #scroll_assertions | ||
|
|
||
| #children | ||
| ) | ||
| } | ||
| let check_width = gen_check(quote!(size.width), width); | ||
| let check_height = gen_check(quote!(size.height), height); | ||
| let check_x = gen_check(quote!(location.x), x); | ||
| let check_y = gen_check(quote!(location.y), y); | ||
|
|
||
| quote!( | ||
| let layout = taffy.layout(#ident).unwrap(); | ||
| #check_width | ||
| #check_height | ||
| #check_x | ||
| #check_y | ||
| #scroll_assertions | ||
|
|
||
| #children | ||
| ) | ||
| } | ||
|
|
||
| fn generate_node(ident: &str, node: &Value) -> TokenStream { | ||
|
|
@@ -439,27 +447,25 @@ fn generate_node(ident: &str, node: &Value) -> TokenStream { | |
| _ => quote!(), | ||
| }; | ||
|
|
||
| fn quote_overflow(overflow: &Value) -> Option<TokenStream> { | ||
| fn quote_overflow(overflow: &Value) -> (TokenStream, bool) { | ||
| match overflow { | ||
| Value::String(ref value) => match value.as_ref() { | ||
| "hidden" => Some(quote!(taffy::style::Overflow::Hidden)), | ||
| "scroll" => Some(quote!(taffy::style::Overflow::Scroll)), | ||
| "auto" => Some(quote!(taffy::style::Overflow::Auto)), | ||
| _ => None, | ||
| "hidden" => (quote!(taffy::style::Overflow::Hidden), true), | ||
| "scroll" => (quote!(taffy::style::Overflow::Scroll), true), | ||
| "auto" => (quote!(taffy::style::Overflow::Auto), true), | ||
| "clip" => (quote!(taffy::style::Overflow::Clip), false), | ||
| _ => (quote!(taffy::style::Overflow::Visible), false), | ||
| }, | ||
| _ => None, | ||
| _ => (quote!(taffy::style::Overflow::Visible), false), | ||
| } | ||
| } | ||
| let overflow_x = quote_overflow(&style["overflowX"]); | ||
| let overflow_y = quote_overflow(&style["overflowY"]); | ||
| let (overflow, scrollbar_width) = if overflow_x.is_some() || overflow_y.is_some() { | ||
| let overflow_x = overflow_x.unwrap_or(quote!(taffy::style::Overflow::Visible)); | ||
| let overflow_y = overflow_y.unwrap_or(quote!(taffy::style::Overflow::Visible)); | ||
| let overflow = quote!(overflow: taffy::geometry::Point { x: #overflow_x, y: #overflow_y },); | ||
| let scrollbar_width = quote_number_prop("scrollbar_width", style, |value: f32| quote!(#value)); | ||
| (overflow, scrollbar_width) | ||
| let (overflow_x, x_scroll_container) = quote_overflow(&style["overflowX"]); | ||
| let (overflow_y, y_scroll_container) = quote_overflow(&style["overflowY"]); | ||
| let overflow = quote!(overflow: taffy::geometry::Point { x: #overflow_x, y: #overflow_y },); | ||
| let scrollbar_width = if x_scroll_container | y_scroll_container { | ||
| quote_number_prop("scrollbar_width", style, |value: f32| quote!(#value)) | ||
| } else { | ||
| (quote!(), quote!()) | ||
| quote!() | ||
| }; | ||
|
|
||
| let text_align = match style["textAlign"] { | ||
|
|
@@ -610,7 +616,8 @@ fn generate_node(ident: &str, node: &Value) -> TokenStream { | |
| let text_content = get_string_value("text_content", node); | ||
| let writing_mode = get_string_value("writingMode", style); | ||
| let raw_aspect_ratio = get_number_value("aspect_ratio", style); | ||
| let node_context: Option<_> = text_content.map(|text| generate_node_context(text, writing_mode, raw_aspect_ratio)); | ||
| let node_context: Option<_> = | ||
| text_content.map(|text| generate_node_context(ident, text, writing_mode, raw_aspect_ratio)); | ||
|
|
||
| edges_quoted!(style, margin, generate_length_percentage_auto, quote!(zero())); | ||
| edges_quoted!(style, padding, generate_length_percentage, quote!(zero())); | ||
|
|
@@ -684,8 +691,11 @@ fn generate_node(ident: &str, node: &Value) -> TokenStream { | |
| #children_body | ||
| let #ident = taffy.new_with_children(#style,#children).unwrap(); | ||
| ) | ||
| } else if node_context.is_some() { | ||
| quote!(let #ident = taffy.new_leaf_with_context(#style,#node_context,).unwrap();) | ||
| } else if let Some((temps, expr)) = node_context { | ||
| quote!( | ||
| #temps | ||
| let #ident = taffy.new_leaf_with_context(#style,#expr,).unwrap(); | ||
| ) | ||
| } else { | ||
| quote!(let #ident = taffy.new_leaf(#style).unwrap();) | ||
| } | ||
|
|
@@ -922,7 +932,12 @@ fn generate_scalar_definition(track_definition: &serde_json::Map<String, Value>) | |
| } | ||
| } | ||
|
|
||
| fn generate_node_context(text_content: &str, writing_mode: Option<&str>, aspect_ratio: Option<f32>) -> TokenStream { | ||
| fn generate_node_context( | ||
| node_ident: &str, | ||
| text_content: &str, | ||
| writing_mode: Option<&str>, | ||
| aspect_ratio: Option<f32>, | ||
| ) -> (TokenStream, TokenStream) { | ||
| let trimmed_text_content = text_content.trim(); | ||
|
|
||
| let writing_mode_token = match writing_mode { | ||
|
|
@@ -935,7 +950,20 @@ fn generate_node_context(text_content: &str, writing_mode: Option<&str>, aspect_ | |
| None => quote!(None), | ||
| }; | ||
|
|
||
| quote!( | ||
| crate::TestNodeContext::ahem_text(#trimmed_text_content, #writing_mode_token) | ||
| ) | ||
| let string_lit = quote!(#trimmed_text_content); | ||
|
|
||
| // If the string literal is too long, rustfmt gives up on the | ||
| // whole line. So pull long string literals out onto their own | ||
| // line so that the Style constructor still gets formatted. | ||
| if string_lit.to_string().len() > 100 { | ||
|
Comment on lines
+953
to
+958
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Here you're quoting the string then converting it back to string to check the length. I think it would be better to do the the length check directly on
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Alternatively you could just format it on a new line unconditionally? It probably doesn't matter too much if we also do this for short strings?
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The quote->format->length bit is because rustfmt gives up when the source representation of the string literal is too long, not when the string contents are too long. There were strings like Another option would be to use the prettyplease crate which appears to have been purpose-built to handle good-enough formatting for generated token streams.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would prefer to avoid
I think that would greatly simplify the test generator and also improve compile times of the tests themselves which are currently generating a lot of duplicate code. |
||
| let temp = format_ident!("{node_ident}_text"); | ||
| (quote!(let #temp = #string_lit;), quote!(crate::TestNodeContext::ahem_text(#temp, #writing_mode_token))) | ||
| } else { | ||
| ( | ||
| quote!(), | ||
| quote!( | ||
| crate::TestNodeContext::ahem_text(#string_lit, #writing_mode_token) | ||
| ), | ||
| ) | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is very nice! At some point I am hoping to be able to print an actual diff between the expected and actual trees. But this is a much easier way of getting some of the benefit of that.