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 set_viewport_hint(vw, 600);
42 let dom = super::super::dom::parse_html(html);
43 let sheet = super::super::css::parse_css(css);
44 let styled = super::super::css::style_tree(&dom, &sheet);
45 let icb = Dimensions {
46 content: Rect {
47 x: 0,
48 y: 0,
49 width: vw,
50 height: 0,
51 },
52 ..Default::default()
53 };
54 let tree = layout_tree(&styled, icb);
55 let b = find(&tree, id)?;
56 let c = &b.dimensions.content;
57 Some((c.x, c.y, c.width, c.height))
58 }
59
60 let cases: &[(&str, &str, i32, &str, (i32, i32, i32, i32))] = &[
62 ("<div id='f'><div id='a'></div><div id='b'></div></div>",
64 "#f{display:flex} #a{width:30px;height:10px} #b{width:40px;height:10px}", 200, "a", (0, 0, 30, 10)),
65 ("<div id='f'><div id='a'></div><div id='b'></div></div>",
66 "#f{display:flex} #a{width:30px;height:10px} #b{width:40px;height:10px}", 200, "b", (30, 0, 40, 10)),
67 ("<div id='f'><div id='a'></div></div>",
69 "#f{display:flex;justify-content:center} #a{width:50px;height:10px}", 200, "a", (75, 0, 50, 10)),
70 ("<div id='f'><div id='a'></div></div>",
72 "#f{display:flex;justify-content:flex-end} #a{width:50px;height:10px}", 200, "a", (150, 0, 50, 10)),
73 ("<div id='f'><div id='a'></div><div id='b'></div></div>",
75 "#f{display:flex;justify-content:space-between} #a{width:20px;height:10px} #b{width:20px;height:10px}", 200, "b", (180, 0, 20, 10)),
76 ("<div id='f'><div id='a'></div><div id='b'></div></div>",
80 "#f{display:flex;justify-content:space-evenly} #a{width:20px;height:10px} #b{width:20px;height:10px}", 200, "a", (53, 0, 20, 10)),
81 ("<div id='f'><div id='a'></div><div id='b'></div></div>",
82 "#f{display:flex;justify-content:space-evenly} #a{width:20px;height:10px} #b{width:20px;height:10px}", 200, "b", (126, 0, 20, 10)),
83 ("<div id='f'><div id='a'></div></div>",
86 "#f{display:flex;justify-content:end} #a{width:50px;height:10px}", 200, "a", (150, 0, 50, 10)),
87 ("<div id='f'><div id='a'></div></div>",
90 "#f{display:flex;height:100px;align-items:end} #a{width:30px;height:20px}", 200, "a", (0, 80, 30, 20)),
91 ("<div id='f'><div id='a'></div></div>",
93 "#f{display:flex;height:100px} #a{width:30px;height:20px;align-self:self-end}", 200, "a", (0, 80, 30, 20)),
94 ("<div id='f'><div id='a'></div></div>",
97 "#f{display:flex;flex-wrap:wrap;justify-content:end} #a{width:50px;height:10px}", 200, "a", (150, 0, 50, 10)),
98 ("<div id='f'><div id='a'></div><div id='b'></div></div>",
101 "#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)),
102 ("<div id='f'><div id='a'></div><div id='b'></div></div>",
104 "#f{display:flex;flex-direction:column} #a{width:30px;height:10px} #b{width:30px;height:15px}", 200, "b", (0, 10, 30, 15)),
105 ("<div id='f'><div id='a'></div></div>",
107 "#f{display:flex;align-items:center;height:100px} #a{width:30px;height:20px}", 200, "a", (0, 40, 30, 20)),
108 ("<div id='f'><div id='a'></div><div id='b'></div></div>",
110 "#f{display:flex;gap:10px} #a{width:20px;height:10px} #b{width:20px;height:10px}", 200, "b", (30, 0, 20, 10)),
111 ("<div id='t'></div>", "#t{height:10px}", 200, "t", (0, 0, 200, 10)),
113 ("<div id='f'><div id='a'></div></div>",
115 "#f{display:flex;height:100px} #a{width:30px}", 200, "a", (0, 0, 30, 100)),
116 ("<div id='f'><div id='a'></div></div>",
118 "#f{display:flex;height:100px} #a{width:30px;height:20px}", 200, "a", (0, 0, 30, 20)),
119 ("<div id='f'><div id='a'></div></div>",
121 "#f{display:flex;flex-direction:column} #a{height:10px}", 200, "a", (0, 0, 200, 10)),
122 ("<div id='f'><div id='a'></div><div id='b'></div></div>",
124 "#f{display:flex} #a{flex:1;height:10px} #b{flex:1;height:10px}", 200, "a", (0, 0, 100, 10)),
125 ("<div id='f'><div id='a'></div><div id='b'></div></div>",
126 "#f{display:flex} #a{flex:1;height:10px} #b{flex:1;height:10px}", 200, "b", (100, 0, 100, 10)),
127 ("<div id='f'><div id='a'></div><div id='b'></div></div>",
129 "#f{display:flex} #a{width:50px;height:10px} #b{flex:1;height:10px}", 200, "b", (50, 0, 150, 10)),
130 ("<div id='f'><div id='a'></div><div id='b'></div></div>",
132 "#f{display:flex} #a{flex-grow:1;height:10px} #b{flex-grow:2;height:10px}", 200, "b", (66, 0, 134, 10)),
133 ("<div id='f'><div id='a'></div><div id='b'></div></div>",
135 "#f{display:flex;flex-direction:column;height:100px} #a{flex:1;width:30px} #b{height:20px;width:30px}", 200, "b", (0, 80, 30, 20)),
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, "b", (20, 15, 200, 10)),
139 ("<div id='p'><div id='a'></div><div id='b'></div></div>",
141 "#a{height:10px} #b{height:10px;position:relative;left:20px;top:5px}", 200, "a", (0, 0, 200, 10)),
142 ("<div id='p'><div id='a'></div></div>",
144 "#p{position:relative;height:100px} #a{position:absolute;top:10px;left:30px;width:40px;height:20px}", 200, "a", (30, 10, 40, 20)),
145 ("<div id='p'><div id='a'></div></div>",
148 "#p{position:relative;height:100px} #a{position:absolute;inset:10px 0 0 30px;width:40px;height:20px}", 200, "a", (30, 10, 40, 20)),
149 ("<div id='p'><div id='a'></div></div>",
151 "#p{position:relative;height:100px} #a{position:absolute;inset:0;width:40px;height:20px}", 200, "a", (0, 0, 40, 20)),
152 ("<div id='p'><div id='a'></div></div>",
154 "#p{position:relative;height:100px} #a{position:absolute;inset:0;left:15px;width:40px;height:20px}", 200, "a", (15, 0, 40, 20)),
155 ("<div id='p'><div id='a'></div></div>",
157 "#p{position:relative;width:200px;height:100px} #a{position:absolute;right:10px;bottom:5px;width:40px;height:20px}", 200, "a", (150, 75, 40, 20)),
158 ("<div id='p'><div id='a'></div><div id='b'></div></div>",
160 "#p{position:relative} #a{position:absolute;top:0;left:0;width:50px;height:50px} #b{height:10px}", 200, "b", (0, 0, 200, 10)),
161 ("<div id='gp'><div id='sp'></div><div id='wrap'><div id='a'></div></div></div>",
164 "#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)),
165 ("<div id='h'></div>",
167 "#h{position:fixed;top:5px;left:8px;width:100px;height:20px}", 200, "h", (8, 5, 100, 20)),
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, "b", (10, 15, 200, 10)),
171 ("<div id='p'><div id='a'></div><div id='b'></div></div>",
173 "#a{height:10px} #b{height:10px;transform:translate(10px,5px)}", 200, "a", (0, 0, 200, 10)),
174 ("<div id='p'><div id='a'></div><div id='b'></div></div>",
176 "#a{height:10px} #b{height:10px;transform:translateX(20px)}", 200, "b", (20, 10, 200, 10)),
177 ("<div id='p'><div id='a'></div><div id='b'></div></div>",
179 "#a{height:10px} #b{width:40px;height:10px;transform:translate(50%,100%)}", 200, "b", (20, 20, 40, 10)),
180 ("<div id='p'><div id='a'></div><div id='b'></div></div>",
182 "#a{display:none;height:10px} #b{height:10px}", 200, "b", (0, 0, 200, 10)),
183 ("<div id='t'></div>",
185 "#t{width:100px;padding:0 10px;box-sizing:border-box;height:30px}", 200, "t", (10, 0, 80, 30)),
186 ("<div id='p'><div id='a'></div></div>",
188 "#a{width:100px;margin:0 auto;height:10px}", 200, "a", (50, 0, 100, 10)),
189 ("<div id='t'></div>",
191 "#t{width:50px;height:50px;padding:10px 0;box-sizing:border-box}", 200, "t", (0, 10, 50, 30)),
192 ("<div id='t'></div>", "#t{height:100vh}", 200, "t", (0, 0, 200, 600)),
194 ("<div id='t'></div>", "#t{height:2rem}", 200, "t", (0, 0, 200, 32)),
196 ("<div id='g'><div id='a'></div><div id='b'></div><div id='c'></div></div>",
198 "#g{display:grid;grid-template-columns:repeat(3,1fr)} #a,#b,#c{height:10px}", 198, "b", (66, 0, 66, 10)),
199 ("<div id='m'><div id='a'></div><div id='b'></div></div>",
203 "#m{column-count:2} #a,#b{height:10px}", 216, "b", (116, 0, 100, 10)),
204 ("<div id='m'><div id='a'></div><div id='b'></div><div id='c'></div></div>",
206 "#m{column-count:2} #a,#b,#c{height:10px}", 216, "c", (0, 10, 100, 10)),
207 ("<div id='m'><div id='a'></div><div id='b'></div></div>",
209 "#m{columns:100px 2;column-gap:0px} #a,#b{height:10px}", 200, "b", (100, 0, 100, 10)),
210 ("<div id='g'><div id='a'></div><div id='b'></div></div>",
212 "#g{display:grid;grid-template-columns:1fr 2fr} #a,#b{height:10px}", 210, "b", (70, 0, 140, 10)),
213 ("<div id='g'><div id='a'></div><div id='b'></div></div>",
215 "#g{display:grid;grid-template-columns:repeat(auto-fit,minmax(80px,1fr))} #a,#b{height:10px}", 200, "b", (100, 0, 100, 10)),
216 ("<div id='g'><div id='a'></div><div id='b'></div></div>",
220 "#g{display:grid;grid-template-columns:50% 1fr} #a,#b{height:10px}", 200, "a", (0, 0, 100, 10)),
221 ("<div id='g'><div id='a'></div><div id='b'></div></div>",
222 "#g{display:grid;grid-template-columns:50% 1fr} #a,#b{height:10px}", 200, "b", (100, 0, 100, 10)),
223 ("<div id='g'><div id='a'></div><div id='b'></div></div>",
228 "#g{display:grid;grid-template-columns:repeat(auto-fit,minmax(50%,1fr))} #a,#b{height:10px}", 200, "b", (100, 0, 100, 10)),
229 ("<div id='t'></div>", "#t{width:160px;aspect-ratio:16/9}", 200, "t", (0, 0, 160, 90)),
231 ("<div id='t'></div>", "#t{width:80px;aspect-ratio:1/1}", 200, "t", (0, 0, 80, 80)),
233 ("<div id='t'></div>", "@media (max-width:300px){#t{height:42px}}", 200, "t", (0, 0, 200, 42)),
235 ("<div id='f'><div id='a'></div><div id='b'></div></div>",
237 "#f{display:flex;flex-direction:row-reverse} #a{width:30px;height:10px} #b{width:40px;height:10px}", 200, "a", (170, 0, 30, 10)),
238 ("<div id='f'><div id='a'></div><div id='b'></div></div>",
239 "#f{display:flex;flex-direction:row-reverse} #a{width:30px;height:10px} #b{width:40px;height:10px}", 200, "b", (130, 0, 40, 10)),
240 ("<div id='f'><div id='a'></div></div>",
242 "#f{display:flex;height:100px} #a{width:30px;height:20px;align-self:flex-end}", 200, "a", (0, 80, 30, 20)),
243 ("<div id='f'><div id='a'></div></div>",
245 "#f{display:flex;height:100px;align-items:flex-start} #a{width:30px;height:20px;align-self:center}", 200, "a", (0, 40, 30, 20)),
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, "c", (0, 10, 100, 10)),
249 ("<div id='f'><div id='a'></div><div id='b'></div><div id='c'></div></div>",
251 "#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)),
252 ("<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, "x", (20, 10, 30, 15)),
256 ("<div id='f'><div id='x'></div><div id='a'></div></div>",
258 "#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)),
259 ("<div id='f'><div id='a'></div><div id='b'></div></div>",
261 "#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)),
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, "x", (12, 8, 20, 10)),
265 ("<div id='g'><div id='x'></div><div id='a'></div><div id='b'></div></div>",
267 "#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)),
268 ("<table id='t'><tr><td id='c1'></td><td id='c2'></td></tr></table>",
271 "#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)),
272 ("<div id='g'><div id='a'></div><div id='b'></div><div id='c'></div></div>",
274 "#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)),
275 ("<div id='g'><div id='a'></div><div id='b'></div></div>",
277 "#g{display:grid;grid-template-columns:repeat(2,1fr)} #a{height:10px} #b{height:10px;grid-column:2}", 200, "b", (100, 0, 100, 10)),
278 ("<div id='g'><div id='a'></div><div id='b'></div></div>",
280 "#g{display:grid;grid-template-columns:1fr} #a{height:10px} #b{height:10px;grid-row:2}", 200, "b", (0, 10, 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, "a", (0, 0, 200, 10)),
284 ("<div id='g'><div id='a'></div><div id='b'></div></div>",
286 "#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)),
287 ("<div id='g'><div id='a'></div><div id='b'></div><div id='c'></div><div id='d'></div></div>",
289 "#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)),
290 ("<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-start} #a{width:60px;height:10px} #b{width:60px;height:10px}", 200, "b", (0, 10, 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:flex-end} #a{width:60px;height:10px} #b{width:60px;height:10px}", 200, "b", (0, 90, 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:center} #a{width:60px;height:10px} #b{width:60px;height:10px}", 200, "a", (0, 40, 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:space-between} #a{width:60px;height:10px} #b{width:60px;height:10px}", 200, "b", (0, 90, 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:stretch} #a{width:60px;height:10px} #b{width:60px;height:10px}", 200, "b", (0, 50, 60, 10)),
306 ("<div id='f'><div id='a'></div><div id='b'></div></div>",
308 "#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)),
309 ("<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:center}", 200, "a", (30, 0, 40, 20)),
313 ("<div id='g'><div id='a'></div></div>",
315 "#g{display:grid;grid-template-columns:100px;width:200px} #a{width:40px;height:20px;justify-self:end}", 200, "a", (60, 0, 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:center}", 200, "a", (0, 20, 40, 20)),
319 ("<div id='g'><div id='a'></div></div>",
321 "#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)),
322 ("<div id='g'><div id='a'></div></div>",
324 "#g{display:grid;grid-template-columns:100px;width:200px;justify-items:center} #a{width:40px;height:20px}", 200, "a", (30, 0, 40, 20)),
325 ("<div id='g'><div id='a'></div></div>",
327 "#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)),
328 ("<div id='g'><div id='a'></div></div>",
332 "#g{display:grid;grid-template-columns:100px;width:200px;justify-content:center} #a{width:100px;height:20px}", 200, "a", (50, 0, 100, 20)),
333 ("<div id='g'><div id='a'></div></div>",
335 "#g{display:grid;grid-template-columns:100px;width:200px;justify-content:end} #a{width:100px;height:20px}", 200, "a", (100, 0, 100, 20)),
336 ("<div id='g'><div id='a'></div><div id='b'></div></div>",
339 "#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)),
340 ("<div id='g'><div id='a'></div></div>",
344 "#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)),
345 ("<div id='g'><div id='a'></div><div id='b'></div></div>",
348 "#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)),
349 ("<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, "a", (0, 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} #b{width:80px;height:10px}", 200, "b", (50, 0, 50, 10)),
356 ("<div id='f'><div id='a'></div><div id='b'></div></div>",
358 "#f{display:flex;width:100px} #a{width:80px;height:10px;flex-shrink:0} #b{width:80px;height:10px}", 200, "b", (80, 0, 20, 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, "b", (60, 0, 30, 10)),
362 ("<div id='f'><div id='a'></div><div id='b'></div><div id='c'></div></div>",
364 "#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)),
365 ("<div id='f'><div id='a'></div><div id='b'></div></div>",
368 "#f{display:flex} #a{width:20px;height:10px;flex-basis:60px} #b{width:30px;height:10px}", 200, "b", (60, 0, 30, 10)),
369 ("<div id='f'><div id='a'></div></div>",
371 "#f{display:flex;width:200px} #a{height:10px;flex-basis:50%}", 200, "a", (0, 0, 100, 10)),
372 ("<div id='f'><div id='a'></div><div id='b'></div></div>",
374 "#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)),
375 ("<div id='f'><div id='a'></div><div id='b'></div></div>",
380 "#f{display:flex;width:200px} #a{height:10px;flex:0 0 40vw} #b{width:10px;height:10px}", 200, "b", (80, 0, 10, 10)),
381 ("<div id='t'><span id='a'></span><span id='b'></span></div>",
384 "#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)),
385 ("<div id='t'><span id='a'></span><span id='b'></span></div>",
387 "#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)),
388 ("<div id='t'><span id='a'></span><span id='b'></span></div>",
390 "#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)),
391 ("<div id='outer'><div id='t'><span id='a'></span><span id='b'></span></div></div>",
393 "#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)),
394 ("<div id='t'></div>", "#t{height:90px;width:auto;aspect-ratio:16/9}", 400, "t", (0, 0, 160, 90)),
397 ("<div id='t'></div>", "#t{height:50px;width:auto;aspect-ratio:2/1}", 400, "t", (0, 0, 100, 50)),
399 ("<div id='t'></div>", "#t{height:60px;width:auto;aspect-ratio:1/1;max-width:40px}", 400, "t", (0, 0, 40, 60)),
401 ("<div id='f'><div id='a'></div><div id='b'></div></div>",
404 "#f{display:flex;width:200px} #a{width:30px;height:10px} #b{width:40px;height:10px;margin-left:auto}", 200, "b", (160, 0, 40, 10)),
405 ("<div id='f'><div id='a'></div></div>",
407 "#f{display:flex;width:200px} #a{width:50px;height:10px;margin:0 auto}", 200, "a", (75, 0, 50, 10)),
408 ("<div id='f'><div id='a'></div><div id='b'></div></div>",
410 "#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)),
411 ("<div id='f'><div id='a'></div><div id='b'></div></div>",
414 "#f{display:flex;width:200px;column-gap:10%} #a{width:30px;height:10px} #b{width:40px;height:10px}", 200, "b", (50, 0, 40, 10)),
415 ("<div id='f'><div id='a'></div><div id='b'></div></div>",
417 "#f{display:flex;width:200px;gap:25%} #a{width:20px;height:10px} #b{width:20px;height:10px}", 200, "b", (70, 0, 20, 10)),
418 ("<div id='g'><div id='a'></div><div id='b'></div></div>",
420 "#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)),
421 ("<div id='t'><span id='a'></span><span id='b'></span></div>",
424 "#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)),
425 ("<div id='t'><span id='a'></span><span id='b'></span></div>",
427 "#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)),
428 ("<div id='g'><div id='a'></div><div id='b'></div></div>",
431 "#g{display:grid;grid-template-rows:minmax(50px,auto)} #a{height:10px} #b{height:10px}", 200, "b", (0, 50, 200, 10)),
432 ("<div id='g'><div id='a'></div><div id='b'></div></div>",
434 "#g{display:grid;grid-template-rows:minmax(10px,30px)} #a{height:60px} #b{height:10px}", 200, "b", (0, 30, 200, 10)),
435 ("<div id='g'><div id='a'></div><div id='b'></div></div>",
437 "#g{display:grid;grid-template-rows:minmax(20px,1fr) 1fr;height:100px} #a{height:10px} #b{height:10px}", 200, "b", (0, 50, 200, 10)),
438 ("<div id='g'><div id='a'></div><div id='b'></div></div>",
443 "#g{display:grid;height:100px;grid-template-rows:50% 50%} #a{height:10px} #b{height:10px}", 200, "b", (0, 50, 200, 10)),
444 ("<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:40px} #a{height:10px} #b{height:10px} #c{height:10px}", 200, "c", (0, 80, 200, 10)),
448 ("<div id='g'><div id='a'></div><div id='b'></div><div id='c'></div></div>",
450 "#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)),
451 ("<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, "c", (100, 0, 100, 10)),
455 ("<div id='g'><div id='a'></div><div id='b'></div><div id='c'></div><div id='d'></div></div>",
457 "#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)),
458 ("<div id='g'><div id='a'></div><div id='b'></div><div id='c'></div><div id='d'></div><div id='e'></div></div>",
462 "#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)),
463 ("<div id='p'><div id='a'></div><div id='b'></div></div>",
466 "#p{width:200px} #a{float:left;width:50px;height:30px} #b{width:100px;height:10px}", 200, "b", (50, 0, 100, 10)),
467 ("<div id='p'><div id='a'></div></div>",
469 "#p{width:200px} #a{float:right;width:50px;height:30px}", 200, "a", (150, 0, 50, 30)),
470 ("<div id='p'><div id='a'></div><div id='c'></div></div>",
472 "#p{width:200px} #a{float:left;width:50px;height:30px} #c{clear:left;width:50px;height:10px}", 200, "c", (0, 30, 50, 10)),
473 ("<div id='p'><div id='a'></div><div id='b'></div></div>",
475 "#p{width:200px} #a{float:left;width:50px;height:10px} #b{width:100px;height:20px}", 200, "p", (0, 0, 200, 20)),
476 ("<div id='p'><div id='a'></div><div id='b'></div></div>",
479 "#p{width:200px} #a{float:inline-start;width:50px;height:30px} #b{width:100px;height:10px}", 200, "b", (50, 0, 100, 10)),
480 ("<div id='p'><div id='a'></div></div>",
482 "#p{width:200px;direction:rtl} #a{float:inline-start;width:50px;height:30px}", 200, "a", (150, 0, 50, 30)),
483 ("<div id='p'><div id='a'></div><div id='b'></div></div>",
485 "#p{width:200px;direction:rtl} #a{float:inline-end;width:50px;height:30px} #b{width:100px;height:10px}", 200, "b", (50, 0, 100, 10)),
486 ("<div id='g'><div id='h'></div><div id='s'></div><div id='c'></div></div>",
489 "#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}",
490 200, "h", (0, 0, 200, 20)),
491 ("<div id='g'><div id='h'></div><div id='s'></div><div id='c'></div></div>",
493 "#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}",
494 200, "s", (0, 20, 100, 30)),
495 ("<div id='g'><div id='h'></div><div id='s'></div><div id='c'></div></div>",
497 "#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}",
498 200, "c", (100, 20, 100, 30)),
499 ("<div id='t'></div>", "#t{width:calc(100px + 50px);height:10px}", 400, "t", (0, 0, 150, 10)),
502 ("<div id='t'></div>", "#t{width:calc(50% - 20px);height:10px}", 400, "t", (0, 0, 180, 10)),
504 ("<div id='t'></div>", "#t{width:min(300px, 50%);height:10px}", 400, "t", (0, 0, 200, 10)),
506 ("<div id='t'></div>", "#t{width:max(300px, 50%);height:10px}", 400, "t", (0, 0, 300, 10)),
508 ("<div id='t'></div>", "#t{width:clamp(100px, 50%, 300px);height:10px}", 400, "t", (0, 0, 200, 10)),
510 ("<div id='t'></div>", "#t{width:clamp(100px, 10%, 300px);height:10px}", 400, "t", (0, 0, 100, 10)),
512 ("<div id='t'></div>", "#t{width:clamp(100px, 90%, 300px);height:10px}", 400, "t", (0, 0, 300, 10)),
514 ("<div id='a'></div><div id='b'></div>",
517 "#a{width:20px;height:10px} #b{width:20px;height:10px;margin-left:calc(10px * 3)}", 400, "b", (30, 10, 20, 10)),
518 ("<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:middle} #t{height:10px}", 400, "t", (200, 45, 200, 10)),
522 ("<table><tr><td id='a'></td><td class='c'><div id='t'></div></td></tr></table>",
524 "#a{width:50px;height:100px} .c{vertical-align:bottom} #t{height:10px}", 400, "t", (200, 90, 200, 10)),
525 ("<table><tr><td id='a'></td><td><div id='t'></div></td></tr></table>",
527 "#a{width:50px;height:100px} #t{height:10px}", 400, "t", (200, 0, 200, 10)),
528 ("<table><tr><td id='a'></td><td id='t' class='c'></td></tr></table>",
530 "#a{width:50px;height:100px} .c{vertical-align:middle}", 400, "t", (200, 0, 200, 100)),
531 ("<table id='tbl'><tr><td id='a'></td><td id='t'></td></tr></table>",
534 "#tbl{border-spacing:10px} #a{width:50px;height:20px} #t{width:50px;height:20px}", 400, "t", (205, 0, 195, 20)),
535 ("<table id='tbl'><tr><td id='a'></td></tr><tr><td id='t'></td></tr></table>",
537 "#tbl{border-spacing:10px 5px} #a{height:20px} #t{height:20px}", 400, "t", (0, 25, 400, 20)),
538 ("<table id='tbl'><tr><td id='a'></td><td id='t'></td></tr></table>",
540 "#tbl{border-collapse:collapse;border-spacing:10px} #a{width:50px;height:20px} #t{width:50px;height:20px}", 400, "t", (199, 0, 200, 20)),
541 ("<div id='wrap'><table id='tbl'><tr><td id='a'></td><td id='t'></td></tr></table></div>",
546 "#wrap{border-collapse:collapse} #tbl{border-spacing:10px} #a{width:50px;height:20px} #t{width:50px;height:20px}", 400, "t", (199, 0, 200, 20)),
547 ("<table><caption id='cap' style='height:15px'></caption><tr><td id='t' style='height:20px'></td></tr></table>",
549 "", 400, "t", (0, 15, 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, "t", (0, 0, 400, 20)),
553 ("<table><caption id='cap' style='height:15px'></caption><tr><td id='t' style='height:20px'></td></tr></table>",
555 "caption{caption-side:bottom}", 400, "cap", (0, 20, 400, 15)),
556 ("<div id='d'><span id='t'>x</span></div>",
558 "#d{direction:rtl;width:100px} #t{width:30px;height:10px}", 400, "t", (70, 0, 30, 10)),
559 ("<div id='d'><span id='t'>x</span></div>",
561 "#d{direction:rtl;text-align:left;width:100px} #t{width:30px;height:10px}", 400, "t", (0, 0, 30, 10)),
562 ("<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>",
565 ".c{visibility:collapse}", 400, "t", (0, 10, 400, 15)),
566 ("<table id='tbl'><tr><td style='width:100px;height:20px'></td><td id='t' style='height:20px'></td></tr></table>",
569 "#tbl{table-layout:fixed}", 400, "t", (100, 0, 300, 20)),
570 ("<table id='tbl'><tr><td style='width:50px;height:10px'></td></tr><tr><td id='t' style='width:200px;height:20px'></td></tr></table>",
573 "#tbl{table-layout:fixed}", 400, "t", (0, 10, 50, 20)),
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'></span></span>",
576 "", 400, "t", (10, 0, 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:middle'></span></span>",
579 "", 400, "t", (10, 10, 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:bottom'></span></span>",
582 "", 400, "t", (10, 20, 10, 10)),
583 ("<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>",
585 "", 400, "a", (0, 0, 10, 30)),
586 ("<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>",
590 "#c{width:250px;text-align-last:right}", 400, "a", (0, 0, 200, 10)),
591 ("<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>",
592 "#c{width:250px;text-align-last:right}", 400, "b", (50, 10, 200, 10)),
593 ("<div id='c'><span id='t' style='display:inline-block;width:30px;height:10px'></span></div>",
596 "#c{width:100px;text-align:end}", 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:start}", 400, "t", (70, 0, 30, 10)),
600 ("<div id='c'><span id='t' style='display:inline-block;width:30px;height:10px'></span></div>",
602 "#c{width:100px;direction:rtl;text-align:end}", 400, "t", (0, 0, 30, 10)),
603 ("<div id='a' hidden style='height:10px'></div><div id='b' style='height:10px'></div>",
607 "", 200, "b", (0, 0, 200, 10)),
608 ("<div id='a' hidden style='display:block;height:10px'></div><div id='b' style='height:10px'></div>",
611 "", 200, "b", (0, 10, 200, 10)),
612 ("<div id='d' dir='rtl'><span id='t' style='width:30px;height:10px'></span></div>",
616 "#d{width:100px}", 400, "t", (70, 0, 30, 10)),
617 ("<div id='d' dir='rtl'><span id='t' style='width:30px;height:10px'></span></div>",
620 "#d{width:100px;direction:ltr}", 400, "t", (0, 0, 30, 10)),
621 ("<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>",
624 "#c{width:200px;text-align:justify}", 400, "a", (0, 0, 100, 10)),
625 ("<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>",
626 "#c{width:200px;text-align:justify}", 400, "b", (190, 0, 10, 10)),
627 ("<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>",
630 "#c{width:200px;text-align:justify}", 400, "c2", (0, 10, 150, 10)),
631 ("<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>",
632 "#c{width:200px;text-align:justify}", 400, "d", (150, 10, 50, 10)),
633 ("<template id='a'><div style='height:10px'></div></template><div id='b' style='height:10px'></div>",
639 "", 200, "b", (0, 0, 200, 10)),
640 ("<noscript>plain text fallback</noscript><div id='b' style='height:10px'></div>",
643 "", 200, "b", (0, 0, 200, 10)),
644 ("<article id='a' style='height:10px'></article><div id='b' style='height:10px'></div>",
648 "", 200, "b", (0, 10, 200, 10)),
649 ("<figure id='a' style='height:10px'></figure><div id='b' style='height:10px'></div>",
651 "", 200, "b", (0, 10, 200, 10)),
652 ("<details id='d'><summary style='height:10px'></summary><div id='c' style='height:20px'></div></details><div id='b' style='height:10px'></div>",
660 "", 200, "b", (0, 10, 200, 10)),
661 ("<details id='d' open><summary style='height:10px'></summary><div id='c' style='height:20px'></div></details><div id='b' style='height:10px'></div>",
664 "", 200, "b", (0, 30, 200, 10)),
665 ("<details id='d' open><summary style='height:10px'></summary><div id='c' style='height:20px'></div></details>",
667 "", 200, "c", (0, 10, 200, 20)),
668 ("<iframe id='f' width='300' height='150'></iframe>",
674 "", 400, "f", (0, 0, 300, 150)),
675 ("<div id='p' style='width:400px'><iframe id='f' width='50%' height='100'></iframe></div>",
677 "", 400, "f", (0, 0, 200, 100)),
678 ("<iframe id='f' width='300' height='150' style='width:50px;height:20px'></iframe>",
681 "", 400, "f", (0, 0, 50, 20)),
682 ];
683 let mut passed = 0;
684 let total = cases.len();
685 for (html, css, vw, id, exp) in cases {
686 match measure(html, css, *vw, id) {
687 Some(d) if d == *exp => passed += 1,
688 Some(d) => crate::println!(
689 "LAYOUT_SELFTEST FAIL: #{} => ({},{},{},{}) want ({},{},{},{})",
690 id,
691 d.0,
692 d.1,
693 d.2,
694 d.3,
695 exp.0,
696 exp.1,
697 exp.2,
698 exp.3
699 ),
700 None => crate::println!("LAYOUT_SELFTEST FAIL: #{} NOT FOUND", id),
701 }
702 }
703
704 let mut extra_passed = 0usize;
711 let mut extra_total = 0usize;
712
713 let det_html = "<div id='c'><div id='a'></div><div id='b'></div></div>";
715 let det_css = "#c{display:flex} #a{width:30px;height:10px} #b{width:40px;height:10px}";
716 extra_total += 1;
717 let r1 = measure(det_html, det_css, 200, "b");
718 let r2 = measure(det_html, det_css, 200, "b");
719 if r1.is_some() && r1 == r2 {
720 extra_passed += 1;
721 } else {
722 crate::println!("LAYOUT_SELFTEST FAIL: determinism r1={:?} r2={:?}", r1, r2);
723 }
724
725 extra_total += 1;
727 {
728 let dom = super::super::dom::parse_html(det_html);
729 let sheet_fresh = super::super::css::parse_css(det_css);
730 let sheet_cached = sheet_fresh.clone(); set_viewport_hint(200, 600);
732 let icb = || Dimensions {
733 content: Rect {
734 x: 0,
735 y: 0,
736 width: 200,
737 height: 0,
738 },
739 ..Default::default()
740 };
741 let styled_a = super::super::css::style_tree(&dom, &sheet_fresh);
742 let styled_b = super::super::css::style_tree(&dom, &sheet_cached);
743 let ta = layout_tree(&styled_a, icb());
744 let tb = layout_tree(&styled_b, icb());
745 let da = find(&ta, "b").map(|b| {
746 let c = &b.dimensions.content;
747 (c.x, c.y, c.width, c.height)
748 });
749 let db = find(&tb, "b").map(|b| {
750 let c = &b.dimensions.content;
751 (c.x, c.y, c.width, c.height)
752 });
753 if da.is_some() && da == db {
754 extra_passed += 1;
755 } else {
756 crate::println!(
757 "LAYOUT_SELFTEST FAIL: css cache equiv da={:?} db={:?}",
758 da,
759 db
760 );
761 }
762 }
763
764 extra_total += 1;
766 {
767 let root = super::super::dom::Node::empty_root();
768 let is_html = matches!(
769 &root.node_type,
770 super::super::dom::NodeType::Element { tag_name, .. } if tag_name == "html"
771 );
772 if is_html && root.children.is_empty() {
773 extra_passed += 1;
774 } else {
775 crate::println!("LAYOUT_SELFTEST FAIL: empty_root shape");
776 }
777 }
778
779 {
783 use crate::kernel::vector_font::{get_vector_string_width, get_vector_string_width_ls};
784 let s = "ABCDE"; let nchars = s.chars().count() as i32;
786 let base = get_vector_string_width(s, 16) as i32;
787
788 extra_total += 1;
790 if get_vector_string_width_ls(s, 16, 0) as i32 == base {
791 extra_passed += 1;
792 } else {
793 crate::println!("LAYOUT_SELFTEST FAIL: letter-spacing ls=0 != base");
794 }
795
796 extra_total += 1;
798 if get_vector_string_width_ls(s, 16, 2) as i32 == base + nchars * 2 {
799 extra_passed += 1;
800 } else {
801 crate::println!("LAYOUT_SELFTEST FAIL: letter-spacing ls=2 width");
802 }
803
804 extra_total += 1;
806 if get_vector_string_width_ls(s, 16, -1) as i32 == (base - nchars).max(0) {
807 extra_passed += 1;
808 } else {
809 crate::println!("LAYOUT_SELFTEST FAIL: letter-spacing ls=-1 width");
810 }
811
812 extra_total += 1;
814 if parse_letter_spacing(Some("normal")) == 0
815 && parse_letter_spacing(Some("3px")) == 3
816 && parse_letter_spacing(Some("-2px")) == -2
817 && parse_letter_spacing(None) == 0
818 && parse_letter_spacing(Some("4")) == 4
819 {
820 extra_passed += 1;
821 } else {
822 crate::println!("LAYOUT_SELFTEST FAIL: parse_letter_spacing");
823 }
824
825 extra_total += 1;
828 if parse_letter_spacing_ctx(Some("calc(1px + 1px)"), 0) == 2
829 && parse_letter_spacing_ctx(Some("min(3px, 5px)"), 0) == 3
830 && parse_letter_spacing_ctx(Some("max(3px, 5px)"), 0) == 5
831 && parse_letter_spacing_ctx(Some("clamp(1px, 10px, 5px)"), 0) == 5
832 {
833 extra_passed += 1;
834 } else {
835 crate::println!("LAYOUT_SELFTEST FAIL: parse_letter_spacing_ctx calc/min/max/clamp");
836 }
837 }
838
839 {
841 use crate::kernel::vector_font::{get_vector_string_width, get_vector_string_width_ls_ws};
842 let s = "AB CD EF"; let base = get_vector_string_width(s, 16) as i32;
844
845 extra_total += 1;
846 if get_vector_string_width_ls_ws(s, 16, 0, 0) as i32 == base {
847 extra_passed += 1;
848 } else {
849 crate::println!("LAYOUT_SELFTEST FAIL: word-spacing ws=0 != base");
850 }
851
852 extra_total += 1;
853 if get_vector_string_width_ls_ws(s, 16, 0, 5) as i32 == base + 2 * 5 {
854 extra_passed += 1;
855 } else {
856 crate::println!("LAYOUT_SELFTEST FAIL: word-spacing ws=5 width");
857 }
858
859 extra_total += 1;
860 if parse_word_spacing(Some("normal")) == 0
861 && parse_word_spacing(Some("3px")) == 3
862 && parse_word_spacing(Some("-2px")) == -2
863 && parse_word_spacing(None) == 0
864 {
865 extra_passed += 1;
866 } else {
867 crate::println!("LAYOUT_SELFTEST FAIL: parse_word_spacing");
868 }
869 }
870
871 {
876 let orig_w = VIEWPORT_HINT_W.load(AtomicOrdering::Relaxed);
881 let orig_h = VIEWPORT_HINT_H.load(AtomicOrdering::Relaxed);
882 set_viewport_hint(1000, 500);
883 let translate_cases: &[(&str, i32, i32)] = &[
884 ("10px", 0, 10),
885 ("50%", 200, 100),
886 ("2rem", 0, 32),
887 ("50vh", 0, 250),
888 ("50vw", 0, 500),
889 ];
890 for (tok, base, want) in translate_cases {
891 extra_total += 1;
892 let got = resolve_translate_len(tok, *base);
893 if got == *want {
894 extra_passed += 1;
895 } else {
896 crate::println!(
897 "LAYOUT_SELFTEST FAIL: resolve_translate_len(`{}`, {}) => {} (want {})",
898 tok, base, got, want
899 );
900 }
901 }
902 VIEWPORT_HINT_W.store(orig_w, AtomicOrdering::Relaxed);
903 VIEWPORT_HINT_H.store(orig_h, AtomicOrdering::Relaxed);
904 }
905
906 (passed + extra_passed, total + extra_total)
907}