1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3 * Copyright (c) 2014 Google, Inc
4 * Written by Simon Glass <sjg@chromium.org>
5 */
6
7 #include <common.h>
8 #include <bzlib.h>
9 #include <dm.h>
10 #include <mapmem.h>
11 #include <os.h>
12 #include <video.h>
13 #include <video_console.h>
14 #include <dm/test.h>
15 #include <dm/uclass-internal.h>
16 #include <test/ut.h>
17
18 /*
19 * These tests use the standard sandbox frame buffer, the resolution of which
20 * is defined in the device tree. This only supports 16bpp so the tests only
21 * test that code path. It would be possible to adjust this fairly easily,
22 * by adjusting the bpix value in struct sandbox_sdl_plat. However the code
23 * in sandbox_sdl_sync() would also need to change to handle the different
24 * surface depth.
25 */
26 /* Basic test of the video uclass */
dm_test_video_base(struct unit_test_state * uts)27 static int dm_test_video_base(struct unit_test_state *uts)
28 {
29 struct video_priv *priv;
30 struct udevice *dev;
31
32 ut_assertok(uclass_get_device(UCLASS_VIDEO, 0, &dev));
33 ut_asserteq(1366, video_get_xsize(dev));
34 ut_asserteq(768, video_get_ysize(dev));
35 priv = dev_get_uclass_priv(dev);
36 ut_asserteq(priv->fb_size, 1366 * 768 * 2);
37
38 return 0;
39 }
40 DM_TEST(dm_test_video_base, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
41
42 /**
43 * compress_frame_buffer() - Compress the frame buffer and return its size
44 *
45 * We want to write tests which perform operations on the video console and
46 * check that the frame buffer ends up with the correct contents. But it is
47 * painful to store 'known good' images for comparison with the frame
48 * buffer. As an alternative, we can compress the frame buffer and check the
49 * size of the compressed data. This provides a pretty good level of
50 * certainty and the resulting tests need only check a single value.
51 *
52 * @dev: Video device
53 * @return compressed size of the frame buffer, or -ve on error
54 */
compress_frame_buffer(struct udevice * dev)55 static int compress_frame_buffer(struct udevice *dev)
56 {
57 struct video_priv *priv = dev_get_uclass_priv(dev);
58 uint destlen;
59 void *dest;
60 int ret;
61
62 destlen = priv->fb_size;
63 dest = malloc(priv->fb_size);
64 if (!dest)
65 return -ENOMEM;
66 ret = BZ2_bzBuffToBuffCompress(dest, &destlen,
67 priv->fb, priv->fb_size,
68 3, 0, 0);
69 free(dest);
70 if (ret)
71 return ret;
72
73 return destlen;
74 }
75
76 /*
77 * Call this function at any point to halt and show the current display. Be
78 * sure to run the test with the -l flag.
79 */
see_output(void)80 static void __maybe_unused see_output(void)
81 {
82 video_sync_all();
83 while (1);
84 }
85
86 /* Select the video console driver to use for a video device */
select_vidconsole(struct unit_test_state * uts,const char * drv_name)87 static int select_vidconsole(struct unit_test_state *uts, const char *drv_name)
88 {
89 struct sandbox_sdl_plat *plat;
90 struct udevice *dev;
91
92 ut_assertok(uclass_find_device(UCLASS_VIDEO, 0, &dev));
93 ut_assert(!device_active(dev));
94 plat = dev_get_platdata(dev);
95 plat->vidconsole_drv_name = "vidconsole0";
96
97 return 0;
98 }
99
100 /* Test text output works on the video console */
dm_test_video_text(struct unit_test_state * uts)101 static int dm_test_video_text(struct unit_test_state *uts)
102 {
103 struct udevice *dev, *con;
104 int i;
105
106 #define WHITE 0xffff
107 #define SCROLL_LINES 100
108
109 ut_assertok(select_vidconsole(uts, "vidconsole0"));
110 ut_assertok(uclass_get_device(UCLASS_VIDEO, 0, &dev));
111 ut_asserteq(46, compress_frame_buffer(dev));
112
113 ut_assertok(uclass_get_device(UCLASS_VIDEO_CONSOLE, 0, &con));
114 vidconsole_putc_xy(con, 0, 0, 'a');
115 ut_asserteq(79, compress_frame_buffer(dev));
116
117 vidconsole_putc_xy(con, 0, 0, ' ');
118 ut_asserteq(46, compress_frame_buffer(dev));
119
120 for (i = 0; i < 20; i++)
121 vidconsole_putc_xy(con, VID_TO_POS(i * 8), 0, ' ' + i);
122 ut_asserteq(273, compress_frame_buffer(dev));
123
124 vidconsole_set_row(con, 0, WHITE);
125 ut_asserteq(46, compress_frame_buffer(dev));
126
127 for (i = 0; i < 20; i++)
128 vidconsole_putc_xy(con, VID_TO_POS(i * 8), 0, ' ' + i);
129 ut_asserteq(273, compress_frame_buffer(dev));
130
131 return 0;
132 }
133 DM_TEST(dm_test_video_text, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
134
135 /* Test handling of special characters in the console */
dm_test_video_chars(struct unit_test_state * uts)136 static int dm_test_video_chars(struct unit_test_state *uts)
137 {
138 struct udevice *dev, *con;
139 const char *test_string = "Well\b\b\b\bxhe is\r \n\ta very \amodest \bman\n\t\tand Has much to\b\bto be modest about.";
140
141 ut_assertok(select_vidconsole(uts, "vidconsole0"));
142 ut_assertok(uclass_get_device(UCLASS_VIDEO, 0, &dev));
143 ut_assertok(uclass_get_device(UCLASS_VIDEO_CONSOLE, 0, &con));
144 vidconsole_put_string(con, test_string);
145 ut_asserteq(466, compress_frame_buffer(dev));
146
147 return 0;
148 }
149 DM_TEST(dm_test_video_chars, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
150
151 #ifdef CONFIG_VIDEO_ANSI
152 #define ANSI_ESC "\x1b"
153 /* Test handling of ANSI escape sequences */
dm_test_video_ansi(struct unit_test_state * uts)154 static int dm_test_video_ansi(struct unit_test_state *uts)
155 {
156 struct udevice *dev, *con;
157
158 ut_assertok(select_vidconsole(uts, "vidconsole0"));
159 ut_assertok(uclass_get_device(UCLASS_VIDEO, 0, &dev));
160 ut_assertok(uclass_get_device(UCLASS_VIDEO_CONSOLE, 0, &con));
161
162 /* reference clear: */
163 video_clear(con->parent);
164 video_sync(con->parent, false);
165 ut_asserteq(46, compress_frame_buffer(dev));
166
167 /* test clear escape sequence: [2J */
168 vidconsole_put_string(con, "A\tB\tC"ANSI_ESC"[2J");
169 ut_asserteq(46, compress_frame_buffer(dev));
170
171 /* test set-cursor: [%d;%df */
172 vidconsole_put_string(con, "abc"ANSI_ESC"[2;2fab"ANSI_ESC"[4;4fcd");
173 ut_asserteq(143, compress_frame_buffer(dev));
174
175 /* test colors (30-37 fg color, 40-47 bg color) */
176 vidconsole_put_string(con, ANSI_ESC"[30;41mfoo"); /* black on red */
177 vidconsole_put_string(con, ANSI_ESC"[33;44mbar"); /* yellow on blue */
178 ut_asserteq(272, compress_frame_buffer(dev));
179
180 return 0;
181 }
182 DM_TEST(dm_test_video_ansi, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
183 #endif
184
185 /**
186 * check_vidconsole_output() - Run a text console test
187 *
188 * @uts: Test state
189 * @rot: Console rotation (0, 90, 180, 270)
190 * @wrap_size: Expected size of compressed frame buffer for the wrap test
191 * @scroll_size: Same for the scroll test
192 * @return 0 on success
193 */
check_vidconsole_output(struct unit_test_state * uts,int rot,int wrap_size,int scroll_size)194 static int check_vidconsole_output(struct unit_test_state *uts, int rot,
195 int wrap_size, int scroll_size)
196 {
197 struct udevice *dev, *con;
198 struct sandbox_sdl_plat *plat;
199 int i;
200
201 ut_assertok(uclass_find_device(UCLASS_VIDEO, 0, &dev));
202 ut_assert(!device_active(dev));
203 plat = dev_get_platdata(dev);
204 plat->rot = rot;
205
206 ut_assertok(uclass_get_device(UCLASS_VIDEO, 0, &dev));
207 ut_assertok(uclass_get_device(UCLASS_VIDEO_CONSOLE, 0, &con));
208 ut_asserteq(46, compress_frame_buffer(dev));
209
210 /* Check display wrap */
211 for (i = 0; i < 120; i++)
212 vidconsole_put_char(con, 'A' + i % 50);
213 ut_asserteq(wrap_size, compress_frame_buffer(dev));
214
215 /* Check display scrolling */
216 for (i = 0; i < SCROLL_LINES; i++) {
217 vidconsole_put_char(con, 'A' + i % 50);
218 vidconsole_put_char(con, '\n');
219 }
220 ut_asserteq(scroll_size, compress_frame_buffer(dev));
221
222 /* If we scroll enough, the screen becomes blank again */
223 for (i = 0; i < SCROLL_LINES; i++)
224 vidconsole_put_char(con, '\n');
225 ut_asserteq(46, compress_frame_buffer(dev));
226
227 return 0;
228 }
229
230 /* Test text output through the console uclass */
dm_test_video_context(struct unit_test_state * uts)231 static int dm_test_video_context(struct unit_test_state *uts)
232 {
233 ut_assertok(select_vidconsole(uts, "vidconsole0"));
234 ut_assertok(check_vidconsole_output(uts, 0, 788, 453));
235
236 return 0;
237 }
238 DM_TEST(dm_test_video_context, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
239
240 /* Test rotated text output through the console uclass */
dm_test_video_rotation1(struct unit_test_state * uts)241 static int dm_test_video_rotation1(struct unit_test_state *uts)
242 {
243 ut_assertok(check_vidconsole_output(uts, 1, 1112, 680));
244
245 return 0;
246 }
247 DM_TEST(dm_test_video_rotation1, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
248
249 /* Test rotated text output through the console uclass */
dm_test_video_rotation2(struct unit_test_state * uts)250 static int dm_test_video_rotation2(struct unit_test_state *uts)
251 {
252 ut_assertok(check_vidconsole_output(uts, 2, 785, 446));
253
254 return 0;
255 }
256 DM_TEST(dm_test_video_rotation2, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
257
258 /* Test rotated text output through the console uclass */
dm_test_video_rotation3(struct unit_test_state * uts)259 static int dm_test_video_rotation3(struct unit_test_state *uts)
260 {
261 ut_assertok(check_vidconsole_output(uts, 3, 1134, 681));
262
263 return 0;
264 }
265 DM_TEST(dm_test_video_rotation3, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
266
267 /* Read a file into memory and return a pointer to it */
read_file(struct unit_test_state * uts,const char * fname,ulong * addrp)268 static int read_file(struct unit_test_state *uts, const char *fname,
269 ulong *addrp)
270 {
271 int buf_size = 100000;
272 ulong addr = 0;
273 int size, fd;
274 char *buf;
275
276 buf = map_sysmem(addr, 0);
277 ut_assert(buf != NULL);
278 fd = os_open(fname, OS_O_RDONLY);
279 ut_assert(fd >= 0);
280 size = os_read(fd, buf, buf_size);
281 os_close(fd);
282 ut_assert(size >= 0);
283 ut_assert(size < buf_size);
284 *addrp = addr;
285
286 return 0;
287 }
288
289 /* Test drawing a bitmap file */
dm_test_video_bmp(struct unit_test_state * uts)290 static int dm_test_video_bmp(struct unit_test_state *uts)
291 {
292 struct udevice *dev;
293 ulong addr;
294
295 ut_assertok(uclass_get_device(UCLASS_VIDEO, 0, &dev));
296 ut_assertok(read_file(uts, "tools/logos/denx.bmp", &addr));
297
298 ut_assertok(video_bmp_display(dev, addr, 0, 0, false));
299 ut_asserteq(1368, compress_frame_buffer(dev));
300
301 return 0;
302 }
303 DM_TEST(dm_test_video_bmp, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
304
305 /* Test drawing a compressed bitmap file */
dm_test_video_bmp_comp(struct unit_test_state * uts)306 static int dm_test_video_bmp_comp(struct unit_test_state *uts)
307 {
308 struct udevice *dev;
309 ulong addr;
310
311 ut_assertok(uclass_get_device(UCLASS_VIDEO, 0, &dev));
312 ut_assertok(read_file(uts, "tools/logos/denx-comp.bmp", &addr));
313
314 ut_assertok(video_bmp_display(dev, addr, 0, 0, false));
315 ut_asserteq(1368, compress_frame_buffer(dev));
316
317 return 0;
318 }
319 DM_TEST(dm_test_video_bmp_comp, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
320
321 /* Test TrueType console */
dm_test_video_truetype(struct unit_test_state * uts)322 static int dm_test_video_truetype(struct unit_test_state *uts)
323 {
324 struct udevice *dev, *con;
325 const char *test_string = "Criticism may not be agreeable, but it is necessary. It fulfils the same function as pain in the human body. It calls attention to an unhealthy state of things. Some see private enterprise as a predatory target to be shot, others as a cow to be milked, but few are those who see it as a sturdy horse pulling the wagon. The \aprice OF\b\bof greatness\n\tis responsibility.\n\nBye";
326
327 ut_assertok(uclass_get_device(UCLASS_VIDEO, 0, &dev));
328 ut_assertok(uclass_get_device(UCLASS_VIDEO_CONSOLE, 0, &con));
329 vidconsole_put_string(con, test_string);
330 ut_asserteq(12237, compress_frame_buffer(dev));
331
332 return 0;
333 }
334 DM_TEST(dm_test_video_truetype, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
335
336 /* Test scrolling TrueType console */
dm_test_video_truetype_scroll(struct unit_test_state * uts)337 static int dm_test_video_truetype_scroll(struct unit_test_state *uts)
338 {
339 struct sandbox_sdl_plat *plat;
340 struct udevice *dev, *con;
341 const char *test_string = "Criticism may not be agreeable, but it is necessary. It fulfils the same function as pain in the human body. It calls attention to an unhealthy state of things. Some see private enterprise as a predatory target to be shot, others as a cow to be milked, but few are those who see it as a sturdy horse pulling the wagon. The \aprice OF\b\bof greatness\n\tis responsibility.\n\nBye";
342
343 ut_assertok(uclass_find_device(UCLASS_VIDEO, 0, &dev));
344 ut_assert(!device_active(dev));
345 plat = dev_get_platdata(dev);
346 plat->font_size = 100;
347
348 ut_assertok(uclass_get_device(UCLASS_VIDEO, 0, &dev));
349 ut_assertok(uclass_get_device(UCLASS_VIDEO_CONSOLE, 0, &con));
350 vidconsole_put_string(con, test_string);
351 ut_asserteq(35030, compress_frame_buffer(dev));
352
353 return 0;
354 }
355 DM_TEST(dm_test_video_truetype_scroll, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
356
357 /* Test TrueType backspace, within and across lines */
dm_test_video_truetype_bs(struct unit_test_state * uts)358 static int dm_test_video_truetype_bs(struct unit_test_state *uts)
359 {
360 struct sandbox_sdl_plat *plat;
361 struct udevice *dev, *con;
362 const char *test_string = "...Criticism may or may\b\b\b\b\b\bnot be agreeable, but seldom it is necessary\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\bit is necessary. It fulfils the same function as pain in the human body. It calls attention to an unhealthy state of things.";
363
364 ut_assertok(uclass_find_device(UCLASS_VIDEO, 0, &dev));
365 ut_assert(!device_active(dev));
366 plat = dev_get_platdata(dev);
367 plat->font_size = 100;
368
369 ut_assertok(uclass_get_device(UCLASS_VIDEO, 0, &dev));
370 ut_assertok(uclass_get_device(UCLASS_VIDEO_CONSOLE, 0, &con));
371 vidconsole_put_string(con, test_string);
372 ut_asserteq(29018, compress_frame_buffer(dev));
373
374 return 0;
375 }
376 DM_TEST(dm_test_video_truetype_bs, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
377