1use super::*;
4
5pub fn selftest() -> (usize, usize) {
8 fn box_id<'a>(b: &LayoutBox<'a>) -> Option<&'a str> {
9 let styled = match &b.box_type {
10 BoxType::BlockNode(s)
11 | BoxType::InlineNode(s)
12 | BoxType::InlineBlockNode(s)
13 | BoxType::FlexNode(s)
14 | BoxType::GridNode(s)
15 | BoxType::TableNode(s)
16 | BoxType::TableRowGroupNode(s)
17 | BoxType::TableRowNode(s)
18 | BoxType::TableCellNode(s) => *s,
19 BoxType::AnonymousBlock => return None,
20 };
21 match &styled.node.node_type {
22 NodeType::Element { id, .. } => id.as_deref(),
23 NodeType::Text(_) => None,
24 }
25 }
26 fn find<'a, 'b>(b: &'b LayoutBox<'a>, id: &str) -> Option<&'b LayoutBox<'a>> {
27 if box_id(b) == Some(id) {
28 return Some(b);
29 }
30 for c in &b.children {
31 if let Some(r) = find(c, id) {
32 return Some(r);
33 }
34 }
35 None
36 }
37 fn measure(html: &str, css: &str, vw: i32, id: &str) -> Option<(i32, i32, i32, i32)> {
38 let dom = super::super::dom::parse_html(html);
39 let sheet = super::super::css::parse_css(css);
40 let styled = super::super::css::style_tree(&dom, &sheet);
41 set_viewport_hint(vw, 600);
42 let icb = Dimensions {
43 content: Rect {
44 x: 0,
45 y: 0,
46 width: vw,
47 height: 0,
48 },
49 ..Default::default()
50 };
51 let tree = layout_tree(&styled, icb);
52 let b = find(&tree, id)?;
53 let c = &b.dimensions.content;
54 Some((c.x, c.y, c.width, c.height))
55 }
56
57 let cases: &[(&str, &str, i32, &str, (i32, i32, i32, i32))] = &[
59 ("<div id='f'><div id='a'></div><div id='b'></div></div>",
61 "#f{display:flex} #a{width:30px;height:10px} #b{width:40px;height:10px}", 200, "a", (0, 0, 30, 10)),
62 ("<div id='f'><div id='a'></div><div id='b'></div></div>",
63 "#f{display:flex} #a{width:30px;height:10px} #b{width:40px;height:10px}", 200, "b", (30, 0, 40, 10)),
64 ("<div id='f'><div id='a'></div></div>",
66 "#f{display:flex;justify-content:center} #a{width:50px;height:10px}", 200, "a", (75, 0, 50, 10)),
67 ("<div id='f'><div id='a'></div></div>",
69 "#f{display:flex;justify-content:flex-end} #a{width:50px;height:10px}", 200, "a", (150, 0, 50, 10)),
70 ("<div id='f'><div id='a'></div><div id='b'></div></div>",
72 "#f{display:flex;justify-content:space-between} #a{width:20px;height:10px} #b{width:20px;height:10px}", 200, "b", (180, 0, 20, 10)),
73 ("<div id='f'><div id='a'></div><div id='b'></div></div>",
77 "#f{display:flex;justify-content:space-evenly} #a{width:20px;height:10px} #b{width:20px;height:10px}", 200, "a", (53, 0, 20, 10)),
78 ("<div id='f'><div id='a'></div><div id='b'></div></div>",
79 "#f{display:flex;justify-content:space-evenly} #a{width:20px;height:10px} #b{width:20px;height:10px}", 200, "b", (126, 0, 20, 10)),
80 ("<div id='f'><div id='a'></div></div>",
83 "#f{display:flex;justify-content:end} #a{width:50px;height:10px}", 200, "a", (150, 0, 50, 10)),
84 ("<div id='f'><div id='a'></div></div>",
87 "#f{display:flex;height:100px;align-items:end} #a{width:30px;height:20px}", 200, "a", (0, 80, 30, 20)),
88 ("<div id='f'><div id='a'></div></div>",
90 "#f{display:flex;height:100px} #a{width:30px;height:20px;align-self:self-end}", 200, "a", (0, 80, 30, 20)),
91 ("<div id='f'><div id='a'></div></div>",
94 "#f{display:flex;flex-wrap:wrap;justify-content:end} #a{width:50px;height:10px}", 200, "a", (150, 0, 50, 10)),
95 ("<div id='f'><div id='a'></div><div id='b'></div></div>",
98 "#f{display:flex;flex-wrap:wrap;justify-content:space-evenly} #a{width:20px;height:10px} #b{width:20px;height:10px}", 200, "b", (126, 0, 20, 10)),
99 ("<div id='f'><div id='a'></div><div id='b'></div></div>",
101 "#f{display:flex;flex-direction:column} #a{width:30px;height:10px} #b{width:30px;height:15px}", 200, "b", (0, 10, 30, 15)),
102 ("<div id='f'><div id='a'></div></div>",
104 "#f{display:flex;align-items:center;height:100px} #a{width:30px;height:20px}", 200, "a", (0, 40, 30, 20)),
105 ("<div id='f'><div id='a'></div><div id='b'></div></div>",
107 "#f{display:flex;gap:10px} #a{width:20px;height:10px} #b{width:20px;height:10px}", 200, "b", (30, 0, 20, 10)),
108 ("<div id='t'></div>", "#t{height:10px}", 200, "t", (0, 0, 200, 10)),
110 ("<div id='f'><div id='a'></div></div>",
112 "#f{display:flex;height:100px} #a{width:30px}", 200, "a", (0, 0, 30, 100)),
113 ("<div id='f'><div id='a'></div></div>",
115 "#f{display:flex;height:100px} #a{width:30px;height:20px}", 200, "a", (0, 0, 30, 20)),
116 ("<div id='f'><div id='a'></div></div>",
118 "#f{display:flex;flex-direction:column} #a{height:10px}", 200, "a", (0, 0, 200, 10)),
119 ("<div id='f'><div id='a'></div><div id='b'></div></div>",
121 "#f{display:flex} #a{flex:1;height:10px} #b{flex:1;height:10px}", 200, "a", (0, 0, 100, 10)),
122 ("<div id='f'><div id='a'></div><div id='b'></div></div>",
123 "#f{display:flex} #a{flex:1;height:10px} #b{flex:1;height:10px}", 200, "b", (100, 0, 100, 10)),
124 ("<div id='f'><div id='a'></div><div id='b'></div></div>",
126 "#f{display:flex} #a{width:50px;height:10px} #b{flex:1;height:10px}", 200, "b", (50, 0, 150, 10)),
127 ("<div id='f'><div id='a'></div><div id='b'></div></div>",
129 "#f{display:flex} #a{flex-grow:1;height:10px} #b{flex-grow:2;height:10px}", 200, "b", (66, 0, 134, 10)),
130 ("<div id='f'><div id='a'></div><div id='b'></div></div>",
132 "#f{display:flex;flex-direction:column;height:100px} #a{flex:1;width:30px} #b{height:20px;width:30px}", 200, "b", (0, 80, 30, 20)),
133 ("<div id='p'><div id='a'></div><div id='b'></div></div>",
135 "#a{height:10px} #b{height:10px;position:relative;left:20px;top:5px}", 200, "b", (20, 15, 200, 10)),
136 ("<div id='p'><div id='a'></div><div id='b'></div></div>",
138 "#a{height:10px} #b{height:10px;position:relative;left:20px;top:5px}", 200, "a", (0, 0, 200, 10)),
139 ("<div id='p'><div id='a'></div></div>",
141 "#p{position:relative;height:100px} #a{position:absolute;top:10px;left:30px;width:40px;height:20px}", 200, "a", (30, 10, 40, 20)),
142 ("<div id='p'><div id='a'></div></div>",
145 "#p{position:relative;height:100px} #a{position:absolute;inset:10px 0 0 30px;width:40px;height:20px}", 200, "a", (30, 10, 40, 20)),
146 ("<div id='p'><div id='a'></div></div>",
148 "#p{position:relative;height:100px} #a{position:absolute;inset:0;width:40px;height:20px}", 200, "a", (0, 0, 40, 20)),
149 ("<div id='p'><div id='a'></div></div>",
151 "#p{position:relative;height:100px} #a{position:absolute;inset:0;left:15px;width:40px;height:20px}", 200, "a", (15, 0, 40, 20)),
152 ("<div id='p'><div id='a'></div></div>",
154 "#p{position:relative;width:200px;height:100px} #a{position:absolute;right:10px;bottom:5px;width:40px;height:20px}", 200, "a", (150, 75, 40, 20)),
155 ("<div id='p'><div id='a'></div><div id='b'></div></div>",
157 "#p{position:relative} #a{position:absolute;top:0;left:0;width:50px;height:50px} #b{height:10px}", 200, "b", (0, 0, 200, 10)),
158 ("<div id='gp'><div id='sp'></div><div id='wrap'><div id='a'></div></div></div>",
161 "#gp{position:relative;height:100px} #sp{height:30px} #a{position:absolute;top:10px;left:20px;width:30px;height:15px}", 200, "a", (20, 10, 30, 15)),
162 ("<div id='h'></div>",
164 "#h{position:fixed;top:5px;left:8px;width:100px;height:20px}", 200, "h", (8, 5, 100, 20)),
165 ("<div id='p'><div id='a'></div><div id='b'></div></div>",
167 "#a{height:10px} #b{height:10px;transform:translate(10px,5px)}", 200, "b", (10, 15, 200, 10)),
168 ("<div id='p'><div id='a'></div><div id='b'></div></div>",
170 "#a{height:10px} #b{height:10px;transform:translate(10px,5px)}", 200, "a", (0, 0, 200, 10)),
171 ("<div id='p'><div id='a'></div><div id='b'></div></div>",
173 "#a{height:10px} #b{height:10px;transform:translateX(20px)}", 200, "b", (20, 10, 200, 10)),
174 ("<div id='p'><div id='a'></div><div id='b'></div></div>",
176 "#a{height:10px} #b{width:40px;height:10px;transform:translate(50%,100%)}", 200, "b", (20, 20, 40, 10)),
177 ("<div id='p'><div id='a'></div><div id='b'></div></div>",
179 "#a{display:none;height:10px} #b{height:10px}", 200, "b", (0, 0, 200, 10)),
180 ("<div id='t'></div>",
182 "#t{width:100px;padding:0 10px;box-sizing:border-box;height:30px}", 200, "t", (10, 0, 80, 30)),
183 ("<div id='p'><div id='a'></div></div>",
185 "#a{width:100px;margin:0 auto;height:10px}", 200, "a", (50, 0, 100, 10)),
186 ("<div id='t'></div>",
188 "#t{width:50px;height:50px;padding:10px 0;box-sizing:border-box}", 200, "t", (0, 10, 50, 30)),
189 ("<div id='t'></div>", "#t{height:100vh}", 200, "t", (0, 0, 200, 600)),
191 ("<div id='t'></div>", "#t{height:2rem}", 200, "t", (0, 0, 200, 32)),
193 ("<div id='g'><div id='a'></div><div id='b'></div><div id='c'></div></div>",
195 "#g{display:grid;grid-template-columns:repeat(3,1fr)} #a,#b,#c{height:10px}", 198, "b", (66, 0, 66, 10)),
196 ("<div id='m'><div id='a'></div><div id='b'></div></div>",
200 "#m{column-count:2} #a,#b{height:10px}", 216, "b", (116, 0, 100, 10)),
201 ("<div id='m'><div id='a'></div><div id='b'></div><div id='c'></div></div>",
203 "#m{column-count:2} #a,#b,#c{height:10px}", 216, "c", (0, 10, 100, 10)),
204 ("<div id='m'><div id='a'></div><div id='b'></div></div>",
206 "#m{columns:100px 2;column-gap:0px} #a,#b{height:10px}", 200, "b", (100, 0, 100, 10)),
207 ("<div id='g'><div id='a'></div><div id='b'></div></div>",
209 "#g{display:grid;grid-template-columns:1fr 2fr} #a,#b{height:10px}", 210, "b", (70, 0, 140, 10)),
210 ("<div id='g'><div id='a'></div><div id='b'></div></div>",
212 "#g{display:grid;grid-template-columns:repeat(auto-fit,minmax(80px,1fr))} #a,#b{height:10px}", 200, "b", (100, 0, 100, 10)),
213 ("<div id='g'><div id='a'></div><div id='b'></div></div>",
217 "#g{display:grid;grid-template-columns:50% 1fr} #a,#b{height:10px}", 200, "a", (0, 0, 100, 10)),
218 ("<div id='g'><div id='a'></div><div id='b'></div></div>",
219 "#g{display:grid;grid-template-columns:50% 1fr} #a,#b{height:10px}", 200, "b", (100, 0, 100, 10)),
220 ("<div id='g'><div id='a'></div><div id='b'></div></div>",
225 "#g{display:grid;grid-template-columns:repeat(auto-fit,minmax(50%,1fr))} #a,#b{height:10px}", 200, "b", (100, 0, 100, 10)),
226 ("<div id='t'></div>", "#t{width:160px;aspect-ratio:16/9}", 200, "t", (0, 0, 160, 90)),
228 ("<div id='t'></div>", "#t{width:80px;aspect-ratio:1/1}", 200, "t", (0, 0, 80, 80)),
230 ("<div id='t'></div>", "@media (max-width:300px){#t{height:42px}}", 200, "t", (0, 0, 200, 42)),
232 ("<div id='f'><div id='a'></div><div id='b'></div></div>",
234 "#f{display:flex;flex-direction:row-reverse} #a{width:30px;height:10px} #b{width:40px;height:10px}", 200, "a", (170, 0, 30, 10)),
235 ("<div id='f'><div id='a'></div><div id='b'></div></div>",
236 "#f{display:flex;flex-direction:row-reverse} #a{width:30px;height:10px} #b{width:40px;height:10px}", 200, "b", (130, 0, 40, 10)),
237 ("<div id='f'><div id='a'></div></div>",
239 "#f{display:flex;height:100px} #a{width:30px;height:20px;align-self:flex-end}", 200, "a", (0, 80, 30, 20)),
240 ("<div id='f'><div id='a'></div></div>",
242 "#f{display:flex;height:100px;align-items:flex-start} #a{width:30px;height:20px;align-self:center}", 200, "a", (0, 40, 30, 20)),
243 ("<div id='f'><div id='a'></div><div id='b'></div><div id='c'></div></div>",
245 "#f{display:flex;flex-wrap:wrap} #a{width:100px;height:10px} #b{width:100px;height:10px} #c{width:100px;height:10px}", 200, "c", (0, 10, 100, 10)),
246 ("<div id='f'><div id='a'></div><div id='b'></div><div id='c'></div></div>",
248 "#f{display:flex;flex-wrap:wrap} #a{width:100px;height:10px} #b{width:100px;height:10px} #c{width:100px;height:10px}", 200, "b", (100, 0, 100, 10)),
249 ("<div id='f'><div id='x'></div><div id='a'></div></div>",
252 "#f{display:flex;position:relative;height:100px} #x{position:absolute;top:10px;left:20px;width:30px;height:15px} #a{width:40px;height:10px}", 200, "x", (20, 10, 30, 15)),
253 ("<div id='f'><div id='x'></div><div id='a'></div></div>",
255 "#f{display:flex;position:relative;height:100px} #x{position:absolute;top:10px;left:20px;width:30px;height:15px} #a{width:40px;height:10px}", 200, "a", (0, 0, 40, 10)),
256 ("<div id='f'><div id='a'></div><div id='b'></div></div>",
258 "#f{display:flex} #a{width:40px;height:10px} #b{width:30px;height:10px;position:relative;left:5px;top:3px}", 200, "b", (45, 3, 30, 10)),
259 ("<div id='g'><div id='x'></div><div id='a'></div><div id='b'></div></div>",
261 "#g{display:grid;position:relative;grid-template-columns:repeat(2,1fr)} #x{position:absolute;top:8px;left:12px;width:20px;height:10px} #a,#b{height:10px}", 200, "x", (12, 8, 20, 10)),
262 ("<div id='g'><div id='x'></div><div id='a'></div><div id='b'></div></div>",
264 "#g{display:grid;position:relative;grid-template-columns:repeat(2,1fr)} #x{position:absolute;top:8px;left:12px;width:20px;height:10px} #a,#b{height:10px}", 200, "b", (100, 0, 100, 10)),
265 ("<table id='t'><tr><td id='c1'></td><td id='c2'></td></tr></table>",
268 "#t{position:relative} #c1{width:50px;height:10px} #c2{width:50px;height:10px;position:relative;left:7px;top:4px}", 200, "c2", (107, 4, 100, 10)),
269 ("<div id='g'><div id='a'></div><div id='b'></div><div id='c'></div></div>",
271 "#g{display:grid;grid-template-columns:repeat(2,1fr);grid-template-rows:20px 30px} #a,#b,#c{height:10px}", 200, "c", (0, 20, 100, 10)),
272 ("<div id='g'><div id='a'></div><div id='b'></div></div>",
274 "#g{display:grid;grid-template-columns:repeat(2,1fr)} #a{height:10px} #b{height:10px;grid-column:2}", 200, "b", (100, 0, 100, 10)),
275 ("<div id='g'><div id='a'></div><div id='b'></div></div>",
277 "#g{display:grid;grid-template-columns:1fr} #a{height:10px} #b{height:10px;grid-row:2}", 200, "b", (0, 10, 200, 10)),
278 ("<div id='g'><div id='a'></div><div id='b'></div></div>",
280 "#g{display:grid;grid-template-columns:repeat(3,100px)} #a{height:10px;grid-column:span 2} #b{height:10px}", 400, "a", (0, 0, 200, 10)),
281 ("<div id='g'><div id='a'></div><div id='b'></div></div>",
283 "#g{display:grid;grid-template-columns:repeat(3,100px)} #a{height:10px;grid-column:span 2} #b{height:10px}", 400, "b", (200, 0, 100, 10)),
284 ("<div id='g'><div id='a'></div><div id='b'></div><div id='c'></div><div id='d'></div></div>",
286 "#g{display:grid;grid-template-columns:repeat(2,1fr);grid-template-rows:1fr 1fr;height:100px} #a,#b,#c,#d{height:10px}", 200, "c", (0, 50, 100, 10)),
287 ("<div id='f'><div id='a'></div><div id='b'></div></div>",
290 "#f{display:flex;flex-wrap:wrap;width:100px;height:100px;align-content:flex-start} #a{width:60px;height:10px} #b{width:60px;height:10px}", 200, "b", (0, 10, 60, 10)),
291 ("<div id='f'><div id='a'></div><div id='b'></div></div>",
293 "#f{display:flex;flex-wrap:wrap;width:100px;height:100px;align-content:flex-end} #a{width:60px;height:10px} #b{width:60px;height:10px}", 200, "b", (0, 90, 60, 10)),
294 ("<div id='f'><div id='a'></div><div id='b'></div></div>",
296 "#f{display:flex;flex-wrap:wrap;width:100px;height:100px;align-content:center} #a{width:60px;height:10px} #b{width:60px;height:10px}", 200, "a", (0, 40, 60, 10)),
297 ("<div id='f'><div id='a'></div><div id='b'></div></div>",
299 "#f{display:flex;flex-wrap:wrap;width:100px;height:100px;align-content:space-between} #a{width:60px;height:10px} #b{width:60px;height:10px}", 200, "b", (0, 90, 60, 10)),
300 ("<div id='f'><div id='a'></div><div id='b'></div></div>",
302 "#f{display:flex;flex-wrap:wrap;width:100px;height:100px;align-content:stretch} #a{width:60px;height:10px} #b{width:60px;height:10px}", 200, "b", (0, 50, 60, 10)),
303 ("<div id='f'><div id='a'></div><div id='b'></div></div>",
305 "#f{display:flex;flex-wrap:wrap;width:100px;height:100px;align-content:space-around} #a{width:60px;height:10px} #b{width:60px;height:10px}", 200, "b", (0, 70, 60, 10)),
306 ("<div id='g'><div id='a'></div></div>",
309 "#g{display:grid;grid-template-columns:100px;width:200px} #a{width:40px;height:20px;justify-self:center}", 200, "a", (30, 0, 40, 20)),
310 ("<div id='g'><div id='a'></div></div>",
312 "#g{display:grid;grid-template-columns:100px;width:200px} #a{width:40px;height:20px;justify-self:end}", 200, "a", (60, 0, 40, 20)),
313 ("<div id='g'><div id='a'></div></div>",
315 "#g{display:grid;grid-template-columns:100px;grid-template-rows:60px;width:200px} #a{width:40px;height:20px;align-self:center}", 200, "a", (0, 20, 40, 20)),
316 ("<div id='g'><div id='a'></div></div>",
318 "#g{display:grid;grid-template-columns:100px;grid-template-rows:60px;width:200px} #a{width:40px;height:20px;align-self:end}", 200, "a", (0, 40, 40, 20)),
319 ("<div id='g'><div id='a'></div></div>",
321 "#g{display:grid;grid-template-columns:100px;width:200px;justify-items:center} #a{width:40px;height:20px}", 200, "a", (30, 0, 40, 20)),
322 ("<div id='g'><div id='a'></div></div>",
324 "#g{display:grid;grid-template-columns:100px;grid-template-rows:60px;width:200px;align-items:end} #a{width:40px;height:20px}", 200, "a", (0, 40, 40, 20)),
325 ("<div id='g'><div id='a'></div></div>",
329 "#g{display:grid;grid-template-columns:100px;width:200px;justify-content:center} #a{width:100px;height:20px}", 200, "a", (50, 0, 100, 20)),
330 ("<div id='g'><div id='a'></div></div>",
332 "#g{display:grid;grid-template-columns:100px;width:200px;justify-content:end} #a{width:100px;height:20px}", 200, "a", (100, 0, 100, 20)),
333 ("<div id='g'><div id='a'></div><div id='b'></div></div>",
336 "#g{display:grid;grid-template-columns:50px 50px;width:200px;justify-content:space-between} #a{width:50px;height:20px} #b{width:50px;height:20px}", 200, "b", (150, 0, 50, 20)),
337 ("<div id='g'><div id='a'></div></div>",
341 "#g{display:grid;grid-template-rows:50px;height:150px;width:200px;align-content:center} #a{width:40px;height:50px}", 200, "a", (0, 50, 40, 50)),
342 ("<div id='g'><div id='a'></div><div id='b'></div></div>",
345 "#g{display:grid;grid-template-rows:20px 20px;height:150px;width:200px;align-content:space-between} #a{width:40px;height:20px} #b{width:40px;height:20px}", 200, "b", (0, 130, 40, 20)),
346 ("<div id='f'><div id='a'></div><div id='b'></div></div>",
349 "#f{display:flex;width:100px} #a{width:80px;height:10px} #b{width:80px;height:10px}", 200, "a", (0, 0, 50, 10)),
350 ("<div id='f'><div id='a'></div><div id='b'></div></div>",
352 "#f{display:flex;width:100px} #a{width:80px;height:10px} #b{width:80px;height:10px}", 200, "b", (50, 0, 50, 10)),
353 ("<div id='f'><div id='a'></div><div id='b'></div></div>",
355 "#f{display:flex;width:100px} #a{width:80px;height:10px;flex-shrink:0} #b{width:80px;height:10px}", 200, "b", (80, 0, 20, 10)),
356 ("<div id='f'><div id='a'></div><div id='b'></div><div id='c'></div></div>",
358 "#f{display:flex} #a{width:30px;height:10px} #b{width:30px;height:10px} #c{width:30px;height:10px;order:-1}", 200, "b", (60, 0, 30, 10)),
359 ("<div id='f'><div id='a'></div><div id='b'></div><div id='c'></div></div>",
361 "#f{display:flex} #a{width:30px;height:10px} #b{width:30px;height:10px} #c{width:30px;height:10px;order:-1}", 200, "c", (0, 0, 30, 10)),
362 ("<div id='f'><div id='a'></div><div id='b'></div></div>",
365 "#f{display:flex} #a{width:20px;height:10px;flex-basis:60px} #b{width:30px;height:10px}", 200, "b", (60, 0, 30, 10)),
366 ("<div id='f'><div id='a'></div></div>",
368 "#f{display:flex;width:200px} #a{height:10px;flex-basis:50%}", 200, "a", (0, 0, 100, 10)),
369 ("<div id='f'><div id='a'></div><div id='b'></div></div>",
371 "#f{display:flex;width:200px} #a{height:10px;flex-basis:40px} #b{height:10px;flex-basis:40px;flex-grow:1}", 200, "b", (40, 0, 160, 10)),
372 ("<div id='f'><div id='a'></div><div id='b'></div></div>",
377 "#f{display:flex;width:200px} #a{height:10px;flex:0 0 40vw} #b{width:10px;height:10px}", 200, "b", (80, 0, 10, 10)),
378 ("<div id='t'><span id='a'></span><span id='b'></span></div>",
381 "#t{width:max-content} #a{display:inline-block;width:50px;height:10px} #b{display:inline-block;width:50px;height:10px}", 400, "t", (0, 0, 100, 10)),
382 ("<div id='t'><span id='a'></span><span id='b'></span></div>",
384 "#t{width:min-content} #a{display:inline-block;width:50px;height:10px} #b{display:inline-block;width:50px;height:10px}", 400, "t", (0, 0, 50, 20)),
385 ("<div id='t'><span id='a'></span><span id='b'></span></div>",
387 "#t{width:fit-content} #a{display:inline-block;width:50px;height:10px} #b{display:inline-block;width:50px;height:10px}", 400, "t", (0, 0, 100, 10)),
388 ("<div id='outer'><div id='t'><span id='a'></span><span id='b'></span></div></div>",
390 "#outer{width:70px} #t{width:fit-content} #a{display:inline-block;width:50px;height:10px} #b{display:inline-block;width:50px;height:10px}", 400, "t", (0, 0, 70, 20)),
391 ("<div id='t'></div>", "#t{height:90px;width:auto;aspect-ratio:16/9}", 400, "t", (0, 0, 160, 90)),
394 ("<div id='t'></div>", "#t{height:50px;width:auto;aspect-ratio:2/1}", 400, "t", (0, 0, 100, 50)),
396 ("<div id='t'></div>", "#t{height:60px;width:auto;aspect-ratio:1/1;max-width:40px}", 400, "t", (0, 0, 40, 60)),
398 ("<div id='f'><div id='a'></div><div id='b'></div></div>",
401 "#f{display:flex;width:200px} #a{width:30px;height:10px} #b{width:40px;height:10px;margin-left:auto}", 200, "b", (160, 0, 40, 10)),
402 ("<div id='f'><div id='a'></div></div>",
404 "#f{display:flex;width:200px} #a{width:50px;height:10px;margin:0 auto}", 200, "a", (75, 0, 50, 10)),
405 ("<div id='f'><div id='a'></div><div id='b'></div></div>",
407 "#f{display:flex;flex-direction:column;height:100px} #a{width:10px;height:10px} #b{width:10px;height:20px;margin-top:auto}", 200, "b", (0, 80, 10, 20)),
408 ("<div id='f'><div id='a'></div><div id='b'></div></div>",
411 "#f{display:flex;width:200px;column-gap:10%} #a{width:30px;height:10px} #b{width:40px;height:10px}", 200, "b", (50, 0, 40, 10)),
412 ("<div id='f'><div id='a'></div><div id='b'></div></div>",
414 "#f{display:flex;width:200px;gap:25%} #a{width:20px;height:10px} #b{width:20px;height:10px}", 200, "b", (70, 0, 20, 10)),
415 ("<div id='g'><div id='a'></div><div id='b'></div></div>",
417 "#g{display:grid;width:200px;grid-template-columns:repeat(2,80px);column-gap:10%} #a{height:10px} #b{height:10px}", 200, "b", (100, 0, 80, 10)),
418 ("<div id='t'><span id='a'></span><span id='b'></span></div>",
421 "#t{max-width:min-content} #a{display:inline-block;width:50px;height:10px} #b{display:inline-block;width:50px;height:10px}", 400, "t", (0, 0, 50, 20)),
422 ("<div id='t'><span id='a'></span><span id='b'></span></div>",
424 "#t{width:20px;min-width:max-content} #a{display:inline-block;width:50px;height:10px} #b{display:inline-block;width:50px;height:10px}", 400, "t", (0, 0, 100, 10)),
425 ("<div id='g'><div id='a'></div><div id='b'></div></div>",
428 "#g{display:grid;grid-template-rows:minmax(50px,auto)} #a{height:10px} #b{height:10px}", 200, "b", (0, 50, 200, 10)),
429 ("<div id='g'><div id='a'></div><div id='b'></div></div>",
431 "#g{display:grid;grid-template-rows:minmax(10px,30px)} #a{height:60px} #b{height:10px}", 200, "b", (0, 30, 200, 10)),
432 ("<div id='g'><div id='a'></div><div id='b'></div></div>",
434 "#g{display:grid;grid-template-rows:minmax(20px,1fr) 1fr;height:100px} #a{height:10px} #b{height:10px}", 200, "b", (0, 50, 200, 10)),
435 ("<div id='g'><div id='a'></div><div id='b'></div></div>",
440 "#g{display:grid;height:100px;grid-template-rows:50% 50%} #a{height:10px} #b{height:10px}", 200, "b", (0, 50, 200, 10)),
441 ("<div id='g'><div id='a'></div><div id='b'></div><div id='c'></div></div>",
444 "#g{display:grid;grid-template-columns:200px;grid-auto-rows:40px} #a{height:10px} #b{height:10px} #c{height:10px}", 200, "c", (0, 80, 200, 10)),
445 ("<div id='g'><div id='a'></div><div id='b'></div><div id='c'></div></div>",
447 "#g{display:grid;grid-template-columns:200px;grid-auto-rows:30px 60px} #a{height:10px} #b{height:10px} #c{height:10px}", 200, "c", (0, 90, 200, 10)),
448 ("<div id='g'><div id='a'></div><div id='b'></div><div id='c'></div><div id='d'></div></div>",
451 "#g{display:grid;grid-template-columns:repeat(2,100px);grid-template-rows:50px 50px;grid-auto-flow:column} #a{height:10px} #b{height:10px} #c{height:10px} #d{height:10px}", 200, "c", (100, 0, 100, 10)),
452 ("<div id='g'><div id='a'></div><div id='b'></div><div id='c'></div><div id='d'></div></div>",
454 "#g{display:grid;grid-template-columns:repeat(2,100px);grid-template-rows:50px 50px;grid-auto-flow:column} #a{height:10px} #b{height:10px} #c{height:10px} #d{height:10px}", 200, "b", (0, 50, 100, 10)),
455 ("<div id='g'><div id='a'></div><div id='b'></div><div id='c'></div><div id='d'></div><div id='e'></div></div>",
459 "#g{display:grid;grid-template-columns:repeat(2,100px);grid-template-rows:50px 50px;grid-auto-flow:column;grid-auto-columns:80px} #a{height:10px} #b{height:10px} #c{height:10px} #d{height:10px} #e{height:10px}", 300, "e", (200, 0, 80, 10)),
460 ("<div id='p'><div id='a'></div><div id='b'></div></div>",
463 "#p{width:200px} #a{float:left;width:50px;height:30px} #b{width:100px;height:10px}", 200, "b", (50, 0, 100, 10)),
464 ("<div id='p'><div id='a'></div></div>",
466 "#p{width:200px} #a{float:right;width:50px;height:30px}", 200, "a", (150, 0, 50, 30)),
467 ("<div id='p'><div id='a'></div><div id='c'></div></div>",
469 "#p{width:200px} #a{float:left;width:50px;height:30px} #c{clear:left;width:50px;height:10px}", 200, "c", (0, 30, 50, 10)),
470 ("<div id='p'><div id='a'></div><div id='b'></div></div>",
472 "#p{width:200px} #a{float:left;width:50px;height:10px} #b{width:100px;height:20px}", 200, "p", (0, 0, 200, 20)),
473 ("<div id='p'><div id='a'></div><div id='b'></div></div>",
476 "#p{width:200px} #a{float:inline-start;width:50px;height:30px} #b{width:100px;height:10px}", 200, "b", (50, 0, 100, 10)),
477 ("<div id='p'><div id='a'></div></div>",
479 "#p{width:200px;direction:rtl} #a{float:inline-start;width:50px;height:30px}", 200, "a", (150, 0, 50, 30)),
480 ("<div id='p'><div id='a'></div><div id='b'></div></div>",
482 "#p{width:200px;direction:rtl} #a{float:inline-end;width:50px;height:30px} #b{width:100px;height:10px}", 200, "b", (50, 0, 100, 10)),
483 ("<div id='g'><div id='h'></div><div id='s'></div><div id='c'></div></div>",
486 "#g{display:grid;grid-template-columns:100px 100px;grid-template-rows:20px 30px;grid-template-areas:'header header' 'sidebar content'} #h{grid-area:header} #s{grid-area:sidebar} #c{grid-area:content}",
487 200, "h", (0, 0, 200, 20)),
488 ("<div id='g'><div id='h'></div><div id='s'></div><div id='c'></div></div>",
490 "#g{display:grid;grid-template-columns:100px 100px;grid-template-rows:20px 30px;grid-template-areas:'header header' 'sidebar content'} #h{grid-area:header} #s{grid-area:sidebar} #c{grid-area:content}",
491 200, "s", (0, 20, 100, 30)),
492 ("<div id='g'><div id='h'></div><div id='s'></div><div id='c'></div></div>",
494 "#g{display:grid;grid-template-columns:100px 100px;grid-template-rows:20px 30px;grid-template-areas:'header header' 'sidebar content'} #h{grid-area:header} #s{grid-area:sidebar} #c{grid-area:content}",
495 200, "c", (100, 20, 100, 30)),
496 ("<div id='t'></div>", "#t{width:calc(100px + 50px);height:10px}", 400, "t", (0, 0, 150, 10)),
499 ("<div id='t'></div>", "#t{width:calc(50% - 20px);height:10px}", 400, "t", (0, 0, 180, 10)),
501 ("<div id='t'></div>", "#t{width:min(300px, 50%);height:10px}", 400, "t", (0, 0, 200, 10)),
503 ("<div id='t'></div>", "#t{width:max(300px, 50%);height:10px}", 400, "t", (0, 0, 300, 10)),
505 ("<div id='t'></div>", "#t{width:clamp(100px, 50%, 300px);height:10px}", 400, "t", (0, 0, 200, 10)),
507 ("<div id='t'></div>", "#t{width:clamp(100px, 10%, 300px);height:10px}", 400, "t", (0, 0, 100, 10)),
509 ("<div id='t'></div>", "#t{width:clamp(100px, 90%, 300px);height:10px}", 400, "t", (0, 0, 300, 10)),
511 ("<div id='a'></div><div id='b'></div>",
514 "#a{width:20px;height:10px} #b{width:20px;height:10px;margin-left:calc(10px * 3)}", 400, "b", (30, 10, 20, 10)),
515 ("<table><tr><td id='a'></td><td class='c'><div id='t'></div></td></tr></table>",
518 "#a{width:50px;height:100px} .c{vertical-align:middle} #t{height:10px}", 400, "t", (200, 45, 200, 10)),
519 ("<table><tr><td id='a'></td><td class='c'><div id='t'></div></td></tr></table>",
521 "#a{width:50px;height:100px} .c{vertical-align:bottom} #t{height:10px}", 400, "t", (200, 90, 200, 10)),
522 ("<table><tr><td id='a'></td><td><div id='t'></div></td></tr></table>",
524 "#a{width:50px;height:100px} #t{height:10px}", 400, "t", (200, 0, 200, 10)),
525 ("<table><tr><td id='a'></td><td id='t' class='c'></td></tr></table>",
527 "#a{width:50px;height:100px} .c{vertical-align:middle}", 400, "t", (200, 0, 200, 100)),
528 ("<table id='tbl'><tr><td id='a'></td><td id='t'></td></tr></table>",
531 "#tbl{border-spacing:10px} #a{width:50px;height:20px} #t{width:50px;height:20px}", 400, "t", (205, 0, 195, 20)),
532 ("<table id='tbl'><tr><td id='a'></td></tr><tr><td id='t'></td></tr></table>",
534 "#tbl{border-spacing:10px 5px} #a{height:20px} #t{height:20px}", 400, "t", (0, 25, 400, 20)),
535 ("<table id='tbl'><tr><td id='a'></td><td id='t'></td></tr></table>",
537 "#tbl{border-collapse:collapse;border-spacing:10px} #a{width:50px;height:20px} #t{width:50px;height:20px}", 400, "t", (199, 0, 200, 20)),
538 ("<div id='wrap'><table id='tbl'><tr><td id='a'></td><td id='t'></td></tr></table></div>",
543 "#wrap{border-collapse:collapse} #tbl{border-spacing:10px} #a{width:50px;height:20px} #t{width:50px;height:20px}", 400, "t", (199, 0, 200, 20)),
544 ("<table><caption id='cap' style='height:15px'></caption><tr><td id='t' style='height:20px'></td></tr></table>",
546 "", 400, "t", (0, 15, 400, 20)),
547 ("<table><caption id='cap' style='height:15px'></caption><tr><td id='t' style='height:20px'></td></tr></table>",
549 "caption{caption-side:bottom}", 400, "t", (0, 0, 400, 20)),
550 ("<table><caption id='cap' style='height:15px'></caption><tr><td id='t' style='height:20px'></td></tr></table>",
552 "caption{caption-side:bottom}", 400, "cap", (0, 20, 400, 15)),
553 ("<div id='d'><span id='t'>x</span></div>",
555 "#d{direction:rtl;width:100px} #t{width:30px;height:10px}", 400, "t", (70, 0, 30, 10)),
556 ("<div id='d'><span id='t'>x</span></div>",
558 "#d{direction:rtl;text-align:left;width:100px} #t{width:30px;height:10px}", 400, "t", (0, 0, 30, 10)),
559 ("<table><tr><td style='height:10px'></td></tr><tr class='c'><td style='height:20px'></td></tr><tr><td id='t' style='height:15px'></td></tr></table>",
562 ".c{visibility:collapse}", 400, "t", (0, 10, 400, 15)),
563 ("<table id='tbl'><tr><td style='width:100px;height:20px'></td><td id='t' style='height:20px'></td></tr></table>",
566 "#tbl{table-layout:fixed}", 400, "t", (100, 0, 300, 20)),
567 ("<table id='tbl'><tr><td style='width:50px;height:10px'></td></tr><tr><td id='t' style='width:200px;height:20px'></td></tr></table>",
570 "#tbl{table-layout:fixed}", 400, "t", (0, 10, 50, 20)),
571 ("<span id='wrap'><span id='a' style='display:inline-block;width:10px;height:30px'></span><span id='t' style='display:inline-block;width:10px;height:10px'></span></span>",
573 "", 400, "t", (10, 0, 10, 10)),
574 ("<span id='wrap'><span id='a' style='display:inline-block;width:10px;height:30px'></span><span id='t' style='display:inline-block;width:10px;height:10px;vertical-align:middle'></span></span>",
576 "", 400, "t", (10, 10, 10, 10)),
577 ("<span id='wrap'><span id='a' style='display:inline-block;width:10px;height:30px'></span><span id='t' style='display:inline-block;width:10px;height:10px;vertical-align:bottom'></span></span>",
579 "", 400, "t", (10, 20, 10, 10)),
580 ("<span id='wrap'><span id='a' style='display:inline-block;width:10px;height:30px'></span><span id='t' style='display:inline-block;width:10px;height:10px;vertical-align:middle'></span></span>",
582 "", 400, "a", (0, 0, 10, 30)),
583 ("<div id='c'><span id='a' style='display:inline-block;width:200px;height:10px'></span><span id='b' style='display:inline-block;width:200px;height:10px'></span></div>",
587 "#c{width:250px;text-align-last:right}", 400, "a", (0, 0, 200, 10)),
588 ("<div id='c'><span id='a' style='display:inline-block;width:200px;height:10px'></span><span id='b' style='display:inline-block;width:200px;height:10px'></span></div>",
589 "#c{width:250px;text-align-last:right}", 400, "b", (50, 10, 200, 10)),
590 ("<div id='c'><span id='t' style='display:inline-block;width:30px;height:10px'></span></div>",
593 "#c{width:100px;text-align:end}", 400, "t", (70, 0, 30, 10)),
594 ("<div id='c'><span id='t' style='display:inline-block;width:30px;height:10px'></span></div>",
596 "#c{width:100px;direction:rtl;text-align:start}", 400, "t", (70, 0, 30, 10)),
597 ("<div id='c'><span id='t' style='display:inline-block;width:30px;height:10px'></span></div>",
599 "#c{width:100px;direction:rtl;text-align:end}", 400, "t", (0, 0, 30, 10)),
600 ("<div id='a' hidden style='height:10px'></div><div id='b' style='height:10px'></div>",
604 "", 200, "b", (0, 0, 200, 10)),
605 ("<div id='a' hidden style='display:block;height:10px'></div><div id='b' style='height:10px'></div>",
608 "", 200, "b", (0, 10, 200, 10)),
609 ("<div id='d' dir='rtl'><span id='t' style='width:30px;height:10px'></span></div>",
613 "#d{width:100px}", 400, "t", (70, 0, 30, 10)),
614 ("<div id='d' dir='rtl'><span id='t' style='width:30px;height:10px'></span></div>",
617 "#d{width:100px;direction:ltr}", 400, "t", (0, 0, 30, 10)),
618 ("<div id='c'><span id='a' style='display:inline-block;width:100px;height:10px'></span><span id='b' style='display:inline-block;width:10px;height:10px'></span><span id='c2' style='display:inline-block;width:150px;height:10px'></span><span id='d' style='display:inline-block;width:50px;height:10px'></span></div>",
621 "#c{width:200px;text-align:justify}", 400, "a", (0, 0, 100, 10)),
622 ("<div id='c'><span id='a' style='display:inline-block;width:100px;height:10px'></span><span id='b' style='display:inline-block;width:10px;height:10px'></span><span id='c2' style='display:inline-block;width:150px;height:10px'></span><span id='d' style='display:inline-block;width:50px;height:10px'></span></div>",
623 "#c{width:200px;text-align:justify}", 400, "b", (190, 0, 10, 10)),
624 ("<div id='c'><span id='a' style='display:inline-block;width:100px;height:10px'></span><span id='b' style='display:inline-block;width:10px;height:10px'></span><span id='c2' style='display:inline-block;width:150px;height:10px'></span><span id='d' style='display:inline-block;width:50px;height:10px'></span></div>",
627 "#c{width:200px;text-align:justify}", 400, "c2", (0, 10, 150, 10)),
628 ("<div id='c'><span id='a' style='display:inline-block;width:100px;height:10px'></span><span id='b' style='display:inline-block;width:10px;height:10px'></span><span id='c2' style='display:inline-block;width:150px;height:10px'></span><span id='d' style='display:inline-block;width:50px;height:10px'></span></div>",
629 "#c{width:200px;text-align:justify}", 400, "d", (150, 10, 50, 10)),
630 ("<template id='a'><div style='height:10px'></div></template><div id='b' style='height:10px'></div>",
636 "", 200, "b", (0, 0, 200, 10)),
637 ("<noscript>plain text fallback</noscript><div id='b' style='height:10px'></div>",
640 "", 200, "b", (0, 0, 200, 10)),
641 ("<article id='a' style='height:10px'></article><div id='b' style='height:10px'></div>",
645 "", 200, "b", (0, 10, 200, 10)),
646 ("<figure id='a' style='height:10px'></figure><div id='b' style='height:10px'></div>",
648 "", 200, "b", (0, 10, 200, 10)),
649 ("<details id='d'><summary style='height:10px'></summary><div id='c' style='height:20px'></div></details><div id='b' style='height:10px'></div>",
657 "", 200, "b", (0, 10, 200, 10)),
658 ("<details id='d' open><summary style='height:10px'></summary><div id='c' style='height:20px'></div></details><div id='b' style='height:10px'></div>",
661 "", 200, "b", (0, 30, 200, 10)),
662 ("<details id='d' open><summary style='height:10px'></summary><div id='c' style='height:20px'></div></details>",
664 "", 200, "c", (0, 10, 200, 20)),
665 ("<iframe id='f' width='300' height='150'></iframe>",
671 "", 400, "f", (0, 0, 300, 150)),
672 ("<div id='p' style='width:400px'><iframe id='f' width='50%' height='100'></iframe></div>",
674 "", 400, "f", (0, 0, 200, 100)),
675 ("<iframe id='f' width='300' height='150' style='width:50px;height:20px'></iframe>",
678 "", 400, "f", (0, 0, 50, 20)),
679 ];
680 let mut passed = 0;
681 let total = cases.len();
682 for (html, css, vw, id, exp) in cases {
683 match measure(html, css, *vw, id) {
684 Some(d) if d == *exp => passed += 1,
685 Some(d) => crate::println!(
686 "LAYOUT_SELFTEST FAIL: #{} => ({},{},{},{}) want ({},{},{},{})",
687 id,
688 d.0,
689 d.1,
690 d.2,
691 d.3,
692 exp.0,
693 exp.1,
694 exp.2,
695 exp.3
696 ),
697 None => crate::println!("LAYOUT_SELFTEST FAIL: #{} NOT FOUND", id),
698 }
699 }
700
701 let mut extra_passed = 0usize;
708 let mut extra_total = 0usize;
709
710 let det_html = "<div id='c'><div id='a'></div><div id='b'></div></div>";
712 let det_css = "#c{display:flex} #a{width:30px;height:10px} #b{width:40px;height:10px}";
713 extra_total += 1;
714 let r1 = measure(det_html, det_css, 200, "b");
715 let r2 = measure(det_html, det_css, 200, "b");
716 if r1.is_some() && r1 == r2 {
717 extra_passed += 1;
718 } else {
719 crate::println!("LAYOUT_SELFTEST FAIL: determinism r1={:?} r2={:?}", r1, r2);
720 }
721
722 extra_total += 1;
724 {
725 let dom = super::super::dom::parse_html(det_html);
726 let sheet_fresh = super::super::css::parse_css(det_css);
727 let sheet_cached = sheet_fresh.clone(); set_viewport_hint(200, 600);
729 let icb = || Dimensions {
730 content: Rect {
731 x: 0,
732 y: 0,
733 width: 200,
734 height: 0,
735 },
736 ..Default::default()
737 };
738 let styled_a = super::super::css::style_tree(&dom, &sheet_fresh);
739 let styled_b = super::super::css::style_tree(&dom, &sheet_cached);
740 let ta = layout_tree(&styled_a, icb());
741 let tb = layout_tree(&styled_b, icb());
742 let da = find(&ta, "b").map(|b| {
743 let c = &b.dimensions.content;
744 (c.x, c.y, c.width, c.height)
745 });
746 let db = find(&tb, "b").map(|b| {
747 let c = &b.dimensions.content;
748 (c.x, c.y, c.width, c.height)
749 });
750 if da.is_some() && da == db {
751 extra_passed += 1;
752 } else {
753 crate::println!(
754 "LAYOUT_SELFTEST FAIL: css cache equiv da={:?} db={:?}",
755 da,
756 db
757 );
758 }
759 }
760
761 extra_total += 1;
763 {
764 let root = super::super::dom::Node::empty_root();
765 let is_html = matches!(
766 &root.node_type,
767 super::super::dom::NodeType::Element { tag_name, .. } if tag_name == "html"
768 );
769 if is_html && root.children.is_empty() {
770 extra_passed += 1;
771 } else {
772 crate::println!("LAYOUT_SELFTEST FAIL: empty_root shape");
773 }
774 }
775
776 {
780 use crate::kernel::vector_font::{get_vector_string_width, get_vector_string_width_ls};
781 let s = "ABCDE"; let nchars = s.chars().count() as i32;
783 let base = get_vector_string_width(s, 16) as i32;
784
785 extra_total += 1;
787 if get_vector_string_width_ls(s, 16, 0) as i32 == base {
788 extra_passed += 1;
789 } else {
790 crate::println!("LAYOUT_SELFTEST FAIL: letter-spacing ls=0 != base");
791 }
792
793 extra_total += 1;
795 if get_vector_string_width_ls(s, 16, 2) as i32 == base + nchars * 2 {
796 extra_passed += 1;
797 } else {
798 crate::println!("LAYOUT_SELFTEST FAIL: letter-spacing ls=2 width");
799 }
800
801 extra_total += 1;
803 if get_vector_string_width_ls(s, 16, -1) as i32 == (base - nchars).max(0) {
804 extra_passed += 1;
805 } else {
806 crate::println!("LAYOUT_SELFTEST FAIL: letter-spacing ls=-1 width");
807 }
808
809 extra_total += 1;
811 if parse_letter_spacing(Some("normal")) == 0
812 && parse_letter_spacing(Some("3px")) == 3
813 && parse_letter_spacing(Some("-2px")) == -2
814 && parse_letter_spacing(None) == 0
815 && parse_letter_spacing(Some("4")) == 4
816 {
817 extra_passed += 1;
818 } else {
819 crate::println!("LAYOUT_SELFTEST FAIL: parse_letter_spacing");
820 }
821
822 extra_total += 1;
825 if parse_letter_spacing_ctx(Some("calc(1px + 1px)"), 0) == 2
826 && parse_letter_spacing_ctx(Some("min(3px, 5px)"), 0) == 3
827 && parse_letter_spacing_ctx(Some("max(3px, 5px)"), 0) == 5
828 && parse_letter_spacing_ctx(Some("clamp(1px, 10px, 5px)"), 0) == 5
829 {
830 extra_passed += 1;
831 } else {
832 crate::println!("LAYOUT_SELFTEST FAIL: parse_letter_spacing_ctx calc/min/max/clamp");
833 }
834 }
835
836 {
838 use crate::kernel::vector_font::{get_vector_string_width, get_vector_string_width_ls_ws};
839 let s = "AB CD EF"; let base = get_vector_string_width(s, 16) as i32;
841
842 extra_total += 1;
843 if get_vector_string_width_ls_ws(s, 16, 0, 0) as i32 == base {
844 extra_passed += 1;
845 } else {
846 crate::println!("LAYOUT_SELFTEST FAIL: word-spacing ws=0 != base");
847 }
848
849 extra_total += 1;
850 if get_vector_string_width_ls_ws(s, 16, 0, 5) as i32 == base + 2 * 5 {
851 extra_passed += 1;
852 } else {
853 crate::println!("LAYOUT_SELFTEST FAIL: word-spacing ws=5 width");
854 }
855
856 extra_total += 1;
857 if parse_word_spacing(Some("normal")) == 0
858 && parse_word_spacing(Some("3px")) == 3
859 && parse_word_spacing(Some("-2px")) == -2
860 && parse_word_spacing(None) == 0
861 {
862 extra_passed += 1;
863 } else {
864 crate::println!("LAYOUT_SELFTEST FAIL: parse_word_spacing");
865 }
866 }
867
868 {
873 let orig_w = VIEWPORT_HINT_W.load(AtomicOrdering::Relaxed);
878 let orig_h = VIEWPORT_HINT_H.load(AtomicOrdering::Relaxed);
879 set_viewport_hint(1000, 500);
880 let translate_cases: &[(&str, i32, i32)] = &[
881 ("10px", 0, 10),
882 ("50%", 200, 100),
883 ("2rem", 0, 32),
884 ("50vh", 0, 250),
885 ("50vw", 0, 500),
886 ];
887 for (tok, base, want) in translate_cases {
888 extra_total += 1;
889 let got = resolve_translate_len(tok, *base);
890 if got == *want {
891 extra_passed += 1;
892 } else {
893 crate::println!(
894 "LAYOUT_SELFTEST FAIL: resolve_translate_len(`{}`, {}) => {} (want {})",
895 tok, base, got, want
896 );
897 }
898 }
899 VIEWPORT_HINT_W.store(orig_w, AtomicOrdering::Relaxed);
900 VIEWPORT_HINT_H.store(orig_h, AtomicOrdering::Relaxed);
901 }
902
903 (passed + extra_passed, total + extra_total)
904}