Seregon/StratoSDK

StratoSDK is a framework with a declarative approach similar to Flutter/React, written and designed entirely for Rust.

Rust/27.3 KB/No license
crates/strato-ui-core/src/text_layout_test.rs
StratoSDK / crates / strato-ui-core / src / text_layout_test.rs
1use float_cmp::assert_approx_eq;
2 
3use super::*;
4use crate::{fonts::Weight, App};
5 
6#[test]
7fn test_empty_line() {
8 App::test((), |mut app| async move {
9 app.update(|ctx| {
10 let line_style = LineStyle {
11 font_size: 12.,
12 line_height_ratio: 1.,
13 baseline_ratio: DEFAULT_TOP_BOTTOM_RATIO,
14 fixed_width_tab_size: None,
15 };
16 let styles = [];
17 
18 let layout_cache = LayoutCache::new();
19 let line = layout_cache.layout_line(
20 "",
21 line_style,
22 &styles,
23 f32::MAX,
24 ClipConfig::end(),
25 &ctx.font_cache().text_layout_system(),
26 );
27 
28 // There should be no contents.
29 assert_eq!(line.runs.len(), 0);
30 
31 // It should have the described line style.
32 assert_eq!(line.font_size, line_style.font_size);
33 assert_eq!(line.line_height_ratio, line_style.line_height_ratio);
34 
35 // It should have zero width, but have a height the same as the line height.
36 assert_eq!(
37 line.height(),
38 line_style.font_size * line_style.line_height_ratio
39 );
40 assert_eq!(line.width, 0.);
41 });
42 });
43}
44 
45#[test]
46fn test_empty_text_frame() {
47 App::test((), |mut app| async move {
48 app.update(|ctx| {
49 let line_style = LineStyle {
50 font_size: 12.,
51 line_height_ratio: 1.,
52 baseline_ratio: DEFAULT_TOP_BOTTOM_RATIO,
53 fixed_width_tab_size: None,
54 };
55 let styles = [];
56 
57 let layout_cache = LayoutCache::new();
58 let frame = layout_cache.layout_text(
59 "",
60 line_style,
61 &styles,
62 f32::MAX,
63 f32::MAX,
64 Default::default(),
65 None,
66 &ctx.font_cache().text_layout_system(),
67 );
68 
69 // There should be one line with no contents.
70 assert_eq!(frame.lines.len(), 1);
71 let line = &frame.lines()[0];
72 assert_eq!(line.runs.len(), 0);
73 
74 // It should have the described line style.
75 assert_eq!(line.font_size, line_style.font_size);
76 assert_eq!(line.line_height_ratio, line_style.line_height_ratio);
77 
78 // It should have zero width, but have a height the same as the line height.
79 assert_eq!(
80 line.height(),
81 line_style.font_size * line_style.line_height_ratio
82 );
83 assert_eq!(line.width, 0.);
84 })
85 });
86}
87 
88#[test]
89fn test_cache_key_includes_fixed_width_tab_size() {
90 let text = "abc";
91 let style_runs: &[(Range<usize>, StyleAndFont)] = &[];
92 
93 let key_4 = CacheKeyRef {
94 text,
95 font_size: OrderedFloat(12.),
96 line_height_ratio: OrderedFloat(1.),
97 fixed_width_tab_size: Some(4),
98 style_runs,
99 max_width: OrderedFloat(100.),
100 max_height: None,
101 alignment: TextAlignment::Left,
102 first_line_head_indent: None,
103 clip_config: None,
104 };
105 let key_8 = CacheKeyRef {
106 fixed_width_tab_size: Some(8),
107 ..key_4
108 };
109 
110 assert!(key_4 != key_8);
111}
112 
113#[test]
114fn test_calculate_line_baseline_position() {
115 let baseline_position = default_compute_baseline_position(
116 16., /* font_size */
117 1.2, /* line_height_ratio */
118 12.8, /* ascent */
119 3.2, /* descent */
120 );
121 // In the default case, we center the text within the line (top padding = font_size * line_height_ratio / 2).
122 // Then, we move the baseline down by the ascent.
123 assert_approx_eq!(f32, baseline_position, 14.4);
124}
125 
126#[test]
127fn test_strip_leading_unicode_bom() {
128 let text = "\u{FEFF}Hello world";
129 // Here is how the text is originally styled:
130 // "\u{FEFF}": Black
131 // "Hello ": Bold, White
132 // "world": Black
133 let mut style_runs = vec![
134 // We include empty ranges because when laying out style runs we often have
135 // multiple empty ranges.
136 (
137 0..0,
138 StyleAndFont::new(FamilyId(0), Properties::default(), TextStyle::default()),
139 ),
140 (
141 0..1,
142 StyleAndFont::new(
143 FamilyId(0),
144 Properties::default(),
145 TextStyle::default().with_foreground_color(ColorU::black()),
146 ),
147 ),
148 (
149 1..1,
150 StyleAndFont::new(FamilyId(0), Properties::default(), TextStyle::default()),
151 ),
152 (
153 1..7,
154 StyleAndFont::new(
155 FamilyId(0),
156 Properties::default().weight(Weight::Bold),
157 TextStyle::default().with_foreground_color(ColorU::white()),
158 ),
159 ),
160 (
161 7..7,
162 StyleAndFont::new(FamilyId(0), Properties::default(), TextStyle::default()),
163 ),
164 (
165 7..13,
166 StyleAndFont::new(
167 FamilyId(0),
168 Properties::default(),
169 TextStyle::default().with_foreground_color(ColorU::black()),
170 ),
171 ),
172 ];
173 let (stripped_text, adjusted_style_runs) =
174 strip_leading_unicode_bom(text, style_runs.as_mut_slice());
175 assert_eq!(stripped_text, "Hello world");
176 
177 // Here is how the text should be styled after stripping the leading BOM character:
178 // "Hello ": Bold, White
179 // "world": Black
180 let expected_style_runs = vec![
181 (
182 0..0,
183 StyleAndFont::new(FamilyId(0), Properties::default(), TextStyle::default()),
184 ),
185 (
186 0..0,
187 StyleAndFont::new(
188 FamilyId(0),
189 Properties::default(),
190 TextStyle::default().with_foreground_color(ColorU::black()),
191 ),
192 ),
193 (
194 0..0,
195 StyleAndFont::new(FamilyId(0), Properties::default(), TextStyle::default()),
196 ),
197 (
198 0..6,
199 StyleAndFont::new(
200 FamilyId(0),
201 Properties::default().weight(Weight::Bold),
202 TextStyle::default().with_foreground_color(ColorU::white()),
203 ),
204 ),
205 (
206 6..6,
207 StyleAndFont::new(FamilyId(0), Properties::default(), TextStyle::default()),
208 ),
209 (
210 6..12,
211 StyleAndFont::new(
212 FamilyId(0),
213 Properties::default(),
214 TextStyle::default().with_foreground_color(ColorU::black()),
215 ),
216 ),
217 ];
218 assert_eq!(adjusted_style_runs, Some(expected_style_runs));
219}
220 
221#[test]
222fn test_strip_leading_unicode_bom_with_initial_range() {
223 let text = "\u{FEFF}A";
224 let mut style_runs = vec![
225 // We include these empty ranges because when laying out style runs we often have
226 // multiple empty ranges.
227 (
228 0..0,
229 StyleAndFont::new(FamilyId(0), Properties::default(), TextStyle::default()),
230 ),
231 (
232 0..2,
233 StyleAndFont::new(
234 FamilyId(0),
235 Properties::default(),
236 TextStyle::default().with_foreground_color(ColorU::black()),
237 ),
238 ),
239 ];
240 let (stripped_text, adjusted_style_runs) =
241 strip_leading_unicode_bom(text, style_runs.as_mut_slice());
242 assert_eq!(stripped_text, "A");
243 
244 let expected_style_runs = vec![
245 (
246 0..0,
247 StyleAndFont::new(FamilyId(0), Properties::default(), TextStyle::default()),
248 ),
249 (
250 0..1,
251 StyleAndFont::new(
252 FamilyId(0),
253 Properties::default(),
254 TextStyle::default().with_foreground_color(ColorU::black()),
255 ),
256 ),
257 ];
258 assert_eq!(adjusted_style_runs, Some(expected_style_runs));
259}
260 
261#[test]
262fn test_strip_leading_unicode_bom_with_single_style_run() {
263 let text = "\u{FEFF}Hello world";
264 let mut style_runs = vec![(
265 0..13,
266 StyleAndFont::new(
267 FamilyId(0),
268 Properties::default(),
269 TextStyle::default().with_foreground_color(ColorU::black()),
270 ),
271 )];
272 let (stripped_text, adjusted_style_runs) =
273 strip_leading_unicode_bom(text, style_runs.as_mut_slice());
274 assert_eq!(stripped_text, "Hello world");
275 
276 let expected_style_runs = vec![(
277 0..12,
278 StyleAndFont::new(
279 FamilyId(0),
280 Properties::default(),
281 TextStyle::default().with_foreground_color(ColorU::black()),
282 ),
283 )];
284 assert_eq!(adjusted_style_runs, Some(expected_style_runs));
285}
286