1 use tui::{
2 backend::TestBackend,
3 buffer::Buffer,
4 layout::Rect,
5 style::{Color, Style},
6 symbols,
7 text::Span,
8 widgets::{Axis, Block, Borders, Chart, Dataset, GraphType::Line},
9 Terminal,
10 };
11
create_labels<'a>(labels: &'a [&'a str]) -> Vec<Span<'a>>12 fn create_labels<'a>(labels: &'a [&'a str]) -> Vec<Span<'a>> {
13 labels.iter().map(|l| Span::from(*l)).collect()
14 }
15
16 #[test]
widgets_chart_can_render_on_small_areas()17 fn widgets_chart_can_render_on_small_areas() {
18 let test_case = |width, height| {
19 let backend = TestBackend::new(width, height);
20 let mut terminal = Terminal::new(backend).unwrap();
21 terminal
22 .draw(|f| {
23 let datasets = vec![Dataset::default()
24 .marker(symbols::Marker::Braille)
25 .style(Style::default().fg(Color::Magenta))
26 .data(&[(0.0, 0.0)])];
27 let chart = Chart::new(datasets)
28 .block(Block::default().title("Plot").borders(Borders::ALL))
29 .x_axis(
30 Axis::default()
31 .bounds([0.0, 0.0])
32 .labels(create_labels(&["0.0", "1.0"])),
33 )
34 .y_axis(
35 Axis::default()
36 .bounds([0.0, 0.0])
37 .labels(create_labels(&["0.0", "1.0"])),
38 );
39 f.render_widget(chart, f.size());
40 })
41 .unwrap();
42 };
43 test_case(0, 0);
44 test_case(0, 1);
45 test_case(1, 0);
46 test_case(1, 1);
47 test_case(2, 2);
48 }
49
50 #[test]
widgets_chart_handles_long_labels()51 fn widgets_chart_handles_long_labels() {
52 let test_case = |x_labels, y_labels, lines| {
53 let backend = TestBackend::new(10, 5);
54 let mut terminal = Terminal::new(backend).unwrap();
55 terminal
56 .draw(|f| {
57 let datasets = vec![Dataset::default()
58 .marker(symbols::Marker::Braille)
59 .style(Style::default().fg(Color::Magenta))
60 .data(&[(2.0, 2.0)])];
61 let mut x_axis = Axis::default().bounds([0.0, 1.0]);
62 if let Some((left_label, right_label)) = x_labels {
63 x_axis = x_axis.labels(vec![Span::from(left_label), Span::from(right_label)]);
64 }
65 let mut y_axis = Axis::default().bounds([0.0, 1.0]);
66 if let Some((left_label, right_label)) = y_labels {
67 y_axis = y_axis.labels(vec![Span::from(left_label), Span::from(right_label)]);
68 }
69 let chart = Chart::new(datasets).x_axis(x_axis).y_axis(y_axis);
70 f.render_widget(chart, f.size());
71 })
72 .unwrap();
73 let expected = Buffer::with_lines(lines);
74 terminal.backend().assert_buffer(&expected);
75 };
76 test_case(
77 Some(("AAAA", "B")),
78 None,
79 vec![
80 " ",
81 " ",
82 " ",
83 " ───────",
84 "AAA B",
85 ],
86 );
87 test_case(
88 Some(("A", "BBBB")),
89 None,
90 vec![
91 " ",
92 " ",
93 " ",
94 " ─────────",
95 "A BBBB",
96 ],
97 );
98 test_case(
99 Some(("AAAAAAAAAAA", "B")),
100 None,
101 vec![
102 " ",
103 " ",
104 " ",
105 " ───────",
106 "AAA B",
107 ],
108 );
109 test_case(
110 Some(("A", "B")),
111 Some(("CCCCCCC", "D")),
112 vec![
113 "D │ ",
114 " │ ",
115 "CCC│ ",
116 " └──────",
117 " A B",
118 ],
119 );
120 }
121
122 #[test]
widgets_chart_can_have_axis_with_zero_length_bounds()123 fn widgets_chart_can_have_axis_with_zero_length_bounds() {
124 let backend = TestBackend::new(100, 100);
125 let mut terminal = Terminal::new(backend).unwrap();
126
127 terminal
128 .draw(|f| {
129 let datasets = vec![Dataset::default()
130 .marker(symbols::Marker::Braille)
131 .style(Style::default().fg(Color::Magenta))
132 .data(&[(0.0, 0.0)])];
133 let chart = Chart::new(datasets)
134 .block(Block::default().title("Plot").borders(Borders::ALL))
135 .x_axis(
136 Axis::default()
137 .bounds([0.0, 0.0])
138 .labels(create_labels(&["0.0", "1.0"])),
139 )
140 .y_axis(
141 Axis::default()
142 .bounds([0.0, 0.0])
143 .labels(create_labels(&["0.0", "1.0"])),
144 );
145 f.render_widget(
146 chart,
147 Rect {
148 x: 0,
149 y: 0,
150 width: 100,
151 height: 100,
152 },
153 );
154 })
155 .unwrap();
156 }
157
158 #[test]
widgets_chart_handles_overflows()159 fn widgets_chart_handles_overflows() {
160 let backend = TestBackend::new(80, 30);
161 let mut terminal = Terminal::new(backend).unwrap();
162
163 terminal
164 .draw(|f| {
165 let datasets = vec![Dataset::default()
166 .marker(symbols::Marker::Braille)
167 .style(Style::default().fg(Color::Magenta))
168 .data(&[
169 (1_588_298_471.0, 1.0),
170 (1_588_298_473.0, 0.0),
171 (1_588_298_496.0, 1.0),
172 ])];
173 let chart = Chart::new(datasets)
174 .block(Block::default().title("Plot").borders(Borders::ALL))
175 .x_axis(
176 Axis::default()
177 .bounds([1_588_298_471.0, 1_588_992_600.0])
178 .labels(create_labels(&["1588298471.0", "1588992600.0"])),
179 )
180 .y_axis(
181 Axis::default()
182 .bounds([0.0, 1.0])
183 .labels(create_labels(&["0.0", "1.0"])),
184 );
185 f.render_widget(
186 chart,
187 Rect {
188 x: 0,
189 y: 0,
190 width: 80,
191 height: 30,
192 },
193 );
194 })
195 .unwrap();
196 }
197
198 #[test]
widgets_chart_can_have_empty_datasets()199 fn widgets_chart_can_have_empty_datasets() {
200 let backend = TestBackend::new(100, 100);
201 let mut terminal = Terminal::new(backend).unwrap();
202
203 terminal
204 .draw(|f| {
205 let datasets = vec![Dataset::default().data(&[]).graph_type(Line)];
206 let chart = Chart::new(datasets)
207 .block(
208 Block::default()
209 .title("Empty Dataset With Line")
210 .borders(Borders::ALL),
211 )
212 .x_axis(
213 Axis::default()
214 .bounds([0.0, 0.0])
215 .labels(create_labels(&["0.0", "1.0"])),
216 )
217 .y_axis(
218 Axis::default()
219 .bounds([0.0, 1.0])
220 .labels(create_labels(&["0.0", "1.0"])),
221 );
222 f.render_widget(
223 chart,
224 Rect {
225 x: 0,
226 y: 0,
227 width: 100,
228 height: 100,
229 },
230 );
231 })
232 .unwrap();
233 }
234
235 #[test]
widgets_chart_can_have_a_legend()236 fn widgets_chart_can_have_a_legend() {
237 let backend = TestBackend::new(60, 30);
238 let mut terminal = Terminal::new(backend).unwrap();
239 terminal
240 .draw(|f| {
241 let datasets = vec![
242 Dataset::default()
243 .name("Dataset 1")
244 .style(Style::default().fg(Color::Blue))
245 .data(&[
246 (0.0, 0.0),
247 (10.0, 1.0),
248 (20.0, 2.0),
249 (30.0, 3.0),
250 (40.0, 4.0),
251 (50.0, 5.0),
252 (60.0, 6.0),
253 (70.0, 7.0),
254 (80.0, 8.0),
255 (90.0, 9.0),
256 (100.0, 10.0),
257 ])
258 .graph_type(Line),
259 Dataset::default()
260 .name("Dataset 2")
261 .style(Style::default().fg(Color::Green))
262 .data(&[
263 (0.0, 10.0),
264 (10.0, 9.0),
265 (20.0, 8.0),
266 (30.0, 7.0),
267 (40.0, 6.0),
268 (50.0, 5.0),
269 (60.0, 4.0),
270 (70.0, 3.0),
271 (80.0, 2.0),
272 (90.0, 1.0),
273 (100.0, 0.0),
274 ])
275 .graph_type(Line),
276 ];
277 let chart = Chart::new(datasets)
278 .style(Style::default().bg(Color::White))
279 .block(Block::default().title("Chart Test").borders(Borders::ALL))
280 .x_axis(
281 Axis::default()
282 .bounds([0.0, 100.0])
283 .title(Span::styled("X Axis", Style::default().fg(Color::Yellow)))
284 .labels(create_labels(&["0.0", "50.0", "100.0"])),
285 )
286 .y_axis(
287 Axis::default()
288 .bounds([0.0, 10.0])
289 .title("Y Axis")
290 .labels(create_labels(&["0.0", "5.0", "10.0"])),
291 );
292 f.render_widget(
293 chart,
294 Rect {
295 x: 0,
296 y: 0,
297 width: 60,
298 height: 30,
299 },
300 );
301 })
302 .unwrap();
303 let mut expected = Buffer::with_lines(vec![
304 "┌Chart Test────────────────────────────────────────────────┐",
305 "│10.0│Y Axis ┌─────────┐│",
306 "│ │ •• │Dataset 1││",
307 "│ │ •• │Dataset 2││",
308 "│ │ •• └─────────┘│",
309 "│ │ •• •• │",
310 "│ │ •• •• │",
311 "│ │ •• •• │",
312 "│ │ •• •• │",
313 "│ │ •• •• │",
314 "│ │ •• •• │",
315 "│ │ •• •• │",
316 "│ │ ••• •• │",
317 "│ │ ••• │",
318 "│5.0 │ •• •• │",
319 "│ │ •• •• │",
320 "│ │ ••• •• │",
321 "│ │ •• •• │",
322 "│ │ •• •• │",
323 "│ │ •• •• │",
324 "│ │ •• •• │",
325 "│ │ •• •• │",
326 "│ │ •• •• │",
327 "│ │ •• ••• │",
328 "│ │ •• •• │",
329 "│ │ •• •• │",
330 "│0.0 │• X Axis│",
331 "│ └─────────────────────────────────────────────────────│",
332 "│ 0.0 50.0 100.0 │",
333 "└──────────────────────────────────────────────────────────┘",
334 ]);
335
336 // Set expected backgound color
337 for row in 0..30 {
338 for col in 0..60 {
339 expected.get_mut(col, row).set_bg(Color::White);
340 }
341 }
342
343 // Set expected colors of the first dataset
344 let line1 = vec![
345 (48, 5),
346 (49, 5),
347 (46, 6),
348 (47, 6),
349 (44, 7),
350 (45, 7),
351 (42, 8),
352 (43, 8),
353 (40, 9),
354 (41, 9),
355 (38, 10),
356 (39, 10),
357 (36, 11),
358 (37, 11),
359 (34, 12),
360 (35, 12),
361 (33, 13),
362 (30, 14),
363 (31, 14),
364 (28, 15),
365 (29, 15),
366 (25, 16),
367 (26, 16),
368 (27, 16),
369 (23, 17),
370 (24, 17),
371 (21, 18),
372 (22, 18),
373 (19, 19),
374 (20, 19),
375 (17, 20),
376 (18, 20),
377 (15, 21),
378 (16, 21),
379 (13, 22),
380 (14, 22),
381 (11, 23),
382 (12, 23),
383 (9, 24),
384 (10, 24),
385 (7, 25),
386 (8, 25),
387 (6, 26),
388 ];
389 let legend1 = vec![
390 (49, 2),
391 (50, 2),
392 (51, 2),
393 (52, 2),
394 (53, 2),
395 (54, 2),
396 (55, 2),
397 (56, 2),
398 (57, 2),
399 ];
400 for (col, row) in line1 {
401 expected.get_mut(col, row).set_fg(Color::Blue);
402 }
403 for (col, row) in legend1 {
404 expected.get_mut(col, row).set_fg(Color::Blue);
405 }
406
407 // Set expected colors of the second dataset
408 let line2 = vec![
409 (8, 2),
410 (9, 2),
411 (10, 3),
412 (11, 3),
413 (12, 4),
414 (13, 4),
415 (14, 5),
416 (15, 5),
417 (16, 6),
418 (17, 6),
419 (18, 7),
420 (19, 7),
421 (20, 8),
422 (21, 8),
423 (22, 9),
424 (23, 9),
425 (24, 10),
426 (25, 10),
427 (26, 11),
428 (27, 11),
429 (28, 12),
430 (29, 12),
431 (30, 12),
432 (31, 13),
433 (32, 13),
434 (33, 14),
435 (34, 14),
436 (35, 15),
437 (36, 15),
438 (37, 16),
439 (38, 16),
440 (39, 17),
441 (40, 17),
442 (41, 18),
443 (42, 18),
444 (43, 19),
445 (44, 19),
446 (45, 20),
447 (46, 20),
448 (47, 21),
449 (48, 21),
450 (49, 22),
451 (50, 22),
452 (51, 23),
453 (52, 23),
454 (53, 23),
455 (54, 24),
456 (55, 24),
457 (56, 25),
458 (57, 25),
459 ];
460 let legend2 = vec![
461 (49, 3),
462 (50, 3),
463 (51, 3),
464 (52, 3),
465 (53, 3),
466 (54, 3),
467 (55, 3),
468 (56, 3),
469 (57, 3),
470 ];
471 for (col, row) in line2 {
472 expected.get_mut(col, row).set_fg(Color::Green);
473 }
474 for (col, row) in legend2 {
475 expected.get_mut(col, row).set_fg(Color::Green);
476 }
477
478 // Set expected colors of the x axis
479 let x_axis_title = vec![(53, 26), (54, 26), (55, 26), (56, 26), (57, 26), (58, 26)];
480 for (col, row) in x_axis_title {
481 expected.get_mut(col, row).set_fg(Color::Yellow);
482 }
483
484 terminal.backend().assert_buffer(&expected);
485 }
486