这是indexloc提供的服务,不要输入任何密码
Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
128 changes: 78 additions & 50 deletions scripts/gentest/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)");
}
)
}
Expand Down Expand Up @@ -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);
}
)
};
Comment on lines +275 to +287
Copy link
Collaborator

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.


// 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 {
Expand Down Expand Up @@ -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"] {
Expand Down Expand Up @@ -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()));
Expand Down Expand Up @@ -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();)
}
Expand Down Expand Up @@ -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 {
Expand All @@ -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
Copy link
Collaborator

Choose a reason for hiding this comment

The 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 trimmed_text_content

Copy link
Collaborator

Choose a reason for hiding this comment

The 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?

Copy link
Author

Choose a reason for hiding this comment

The 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 "HH\u{200b}HH\u{200b}HH..." that overflowed rustfmt's cutoff but still had a low character count.

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.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer to avoid prettyplease if possible for test generator compile time reasons. Where I actually want to go with this is:

  • To have the test generator generate a JSON (or XML or some other common data format) representation of the test cases.
  • Have a single implementation of the test running code
  • Have each generated test fn simply call into this single function with the path/key of the data

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)
),
)
}
}
Loading
Loading