⎇ devin/1785205225-fix-grid-track-limit-overflow
src/test.rs
Ñawpa willay
Pi imata llamkarqan
ch'uya
Add `taffy_test_helpers` crate (#755)
1#![allow(dead_code)]
2
3use taffy::{AvailableSpace, NodeId, Size, Style};
4
5/// A shared measure function for tests which means that tests compiled with separate crates
6/// and using different styles of measure function. This saves on compile time when running tests.
7
8#[derive(Debug, Copy, Clone)]
9pub struct TestNodeContext {
10 /// How many times the measure function has been called
11 pub count: usize,
12 /// The measurement data to compute the intrinsic size from
13 pub measure_data: TestMeasureData,
14}
15
16impl TestNodeContext {
17 /// Create a new `TestNodeContext` from `TestMeasureData`
18 pub const fn new(measure_data: TestMeasureData) -> Self {
19 Self { count: 0, measure_data }
20 }
21
22 /// Create a `TestNodeContext` for a zero-sized node
23 pub const fn zero() -> Self {
24 Self::new(TestMeasureData::Zero)
25 }
26
27 /// Create a `TestNodeContext` for a fixed-sized node
28 pub const fn fixed(size: Size<f32>) -> Self {
29 Self::new(TestMeasureData::Fixed(size))
30 }
31
32 /// Create a `TestNodeContext` for a node with a width and aspect-ratio
33 pub const fn aspect_ratio(width: f32, height_ratio: f32) -> Self {
34 let data = AspectRatioMeasureData { width, height_ratio };
35 Self::new(TestMeasureData::AspectRatio(data))
36 }
37
38 /// Create a `TestNodeContext` for a node with text using the Ahem font
39 pub const fn ahem_text(text_content: &'static str, writing_mode: WritingMode) -> Self {
40 let data = AhemTextMeasureData { text_content, writing_mode };
41 Self::new(TestMeasureData::AhemText(data))
42 }
43}
44
45/// The measurement data for the node
46#[derive(Debug, Copy, Clone)]
47pub enum TestMeasureData {
48 /// A zero-sized node
49 Zero,
50 /// A node with a fixed size
51 Fixed(Size<f32>),
52 /// A node with a fixed size
53 AspectRatio(AspectRatioMeasureData),
54 /// A node with text using the Ahem font
55 AhemText(AhemTextMeasureData),
56}
57
58/// A measure function for tests that works with `TestNodeContext`
59pub fn test_measure_function(
60 known_dimensions: Size<Option<f32>>,
61 available_space: Size<AvailableSpace>,
62 _node_id: NodeId,
63 context: Option<&mut TestNodeContext>,
64 _style: &Style,
65) -> Size<f32> {
66 if let Size { width: Some(width), height: Some(height) } = known_dimensions {
67 return Size { width, height };
68 }
69
70 let Some(context) = context else { return known_dimensions.map(|d| d.unwrap_or(0.0)) };
71
72 // Increment count
73 context.count += 1;
74
75 let compute_size = match &context.measure_data {
76 TestMeasureData::Zero => Size::ZERO,
77 TestMeasureData::Fixed(size) => *size,
78 TestMeasureData::AspectRatio(data) => data.measure(known_dimensions),
79 TestMeasureData::AhemText(data) => data.measure(known_dimensions, available_space),
80 };
81
82 Size {
83 width: known_dimensions.width.unwrap_or(compute_size.width),
84 height: known_dimensions.height.unwrap_or(compute_size.height),
85 }
86}
87
88/// Measure data for nodes that returns results based on an intrinsic aspect ratio
89#[derive(Debug, Copy, Clone)]
90pub struct AspectRatioMeasureData {
91 width: f32,
92 height_ratio: f32,
93}
94impl AspectRatioMeasureData {
95 fn measure(&self, known_dimensions: Size<Option<f32>>) -> Size<f32> {
96 let width = known_dimensions.width.unwrap_or(self.width);
97 let height = known_dimensions.height.unwrap_or(width * self.height_ratio);
98 Size { width, height }
99 }
100}
101
102/// Whether text is horizontal or vertical
103#[derive(Debug, Clone, Copy, PartialEq, Eq)]
104pub enum WritingMode {
105 /// Horizontal text
106 Horizontal,
107 /// Vertical text
108 Vertical,
109}
110/// Measure data for nodes contain text using the Ahem testing font
111#[derive(Debug, Clone, Copy)]
112pub struct AhemTextMeasureData {
113 /// The text string
114 pub text_content: &'static str,
115 /// The writing mode
116 pub writing_mode: WritingMode,
117}
118impl AhemTextMeasureData {
119 fn measure(
120 &self,
121 known_dimensions: taffy::Size<Option<f32>>,
122 available_space: taffy::Size<taffy::AvailableSpace>,
123 ) -> taffy::Size<f32> {
124 use taffy::prelude::*;
125 use taffy::AbsoluteAxis;
126
127 const ZWS: char = '\u{200B}';
128 const H_WIDTH: f32 = 10.0;
129 const H_HEIGHT: f32 = 10.0;
130
131 let inline_axis = match self.writing_mode {
132 WritingMode::Horizontal => AbsoluteAxis::Horizontal,
133 WritingMode::Vertical => AbsoluteAxis::Vertical,
134 };
135 let block_axis = inline_axis.other_axis();
136 let lines: Vec<&str> = self.text_content.split(ZWS).collect();
137
138 if lines.is_empty() {
139 return Size::ZERO;
140 }
141
142 let min_line_length: usize = lines.iter().map(|line| line.len()).max().unwrap_or(0);
143 let max_line_length: usize = lines.iter().map(|line| line.len()).sum();
144 let inline_size = known_dimensions
145 .get_abs(inline_axis)
146 .unwrap_or_else(|| match available_space.get_abs(inline_axis) {
147 AvailableSpace::MinContent => min_line_length as f32 * H_WIDTH,
148 AvailableSpace::MaxContent => max_line_length as f32 * H_WIDTH,
149 AvailableSpace::Definite(inline_size) => inline_size.min(max_line_length as f32 * H_WIDTH),
150 })
151 .max(min_line_length as f32 * H_WIDTH);
152 let block_size = known_dimensions.get_abs(block_axis).unwrap_or_else(|| {
153 let inline_line_length = (inline_size / H_WIDTH).floor() as usize;
154 let mut line_count = 1;
155 let mut current_line_length = 0;
156 for line in &lines {
157 if current_line_length + line.len() > inline_line_length {
158 if current_line_length > 0 {
159 line_count += 1
160 };
161 current_line_length = line.len();
162 } else {
163 current_line_length += line.len();
164 };
165 }
166 (line_count as f32) * H_HEIGHT
167 });
168
169 match self.writing_mode {
170 WritingMode::Horizontal => Size { width: inline_size, height: block_size },
171 WritingMode::Vertical => Size { width: block_size, height: inline_size },
172 }
173 }
174}