blob: 7104dff05e8d9e2dc477f3ec8fad8139a340c27e [file] [log] [blame]
Simon Glass65924992023-01-06 08:52:39 -06001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Copyright 2022 Google LLC
4 * Written by Simon Glass <sjg@chromium.org>
5 */
6
7#include <common.h>
8#include <dm.h>
9#include <expo.h>
10#include <menu.h>
11#include <video.h>
12#include <linux/input.h>
13#include <test/suites.h>
14#include <test/ut.h>
15#include "bootstd_common.h"
16#include "../../boot/scene_internal.h"
17
18enum {
19 /* scenes */
20 SCENE1 = 7,
21 SCENE2,
22
23 /* objects */
24 OBJ_LOGO,
25 OBJ_TEXT,
26 OBJ_TEXT2,
27 OBJ_MENU,
28 OBJ_MENU_TITLE,
29
30 /* strings */
31 STR_TEXT,
32 STR_TEXT2,
33 STR_MENU_TITLE,
34 STR_POINTER_TEXT,
35
36 STR_ITEM1_LABEL,
37 STR_ITEM1_DESC,
38 STR_ITEM1_KEY,
39 STR_ITEM1_PREVIEW,
40
41 STR_ITEM2_LABEL,
42 STR_ITEM2_DESC,
43 STR_ITEM2_KEY,
44 STR_ITEM2_PREVIEW,
45
46 /* menu items */
47 ITEM1,
48 ITEM1_LABEL,
49 ITEM1_DESC,
50 ITEM1_KEY,
51 ITEM1_PREVIEW,
52
53 ITEM2,
54 ITEM2_LABEL,
55 ITEM2_DESC,
56 ITEM2_KEY,
57 ITEM2_PREVIEW,
58
59 /* pointer to current item */
60 POINTER_TEXT,
61};
62
63#define BAD_POINTER ((void *)1)
64
65/* names for various things */
66#define EXPO_NAME "my menus"
67#define SCENE_NAME1 "main"
68#define SCENE_NAME2 "second"
69#define SCENE_TITLE "Main Menu"
70#define LOGO_NAME "logo"
71
72/* Check base expo support */
73static int expo_base(struct unit_test_state *uts)
74{
75 struct udevice *dev;
76 struct expo *exp;
77 ulong start_mem;
78 char name[100];
79 int i;
80
81 ut_assertok(uclass_first_device_err(UCLASS_VIDEO, &dev));
82
83 start_mem = ut_check_free();
84
85 exp = NULL;
86 strcpy(name, EXPO_NAME);
87 ut_assertok(expo_new(name, NULL, &exp));
88 *name = '\0';
89 ut_assertnonnull(exp);
90 ut_asserteq(0, exp->scene_id);
91 ut_asserteq(0, exp->next_id);
92
93 /* Make sure the name was allocated */
94 ut_assertnonnull(exp->name);
95 ut_asserteq_str(EXPO_NAME, exp->name);
96
97 ut_assertok(expo_set_display(exp, dev));
98 expo_destroy(exp);
99 ut_assertok(ut_check_delta(start_mem));
100
101 /* test handling out-of-memory conditions */
102 for (i = 0; i < 2; i++) {
103 struct expo *exp2;
104
105 malloc_enable_testing(i);
106 exp2 = BAD_POINTER;
107 ut_asserteq(-ENOMEM, expo_new(EXPO_NAME, NULL, &exp2));
108 ut_asserteq_ptr(BAD_POINTER, exp2);
109 malloc_disable_testing();
110 }
111
112 return 0;
113}
114BOOTSTD_TEST(expo_base, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
115
116/* Check creating a scene */
117static int expo_scene(struct unit_test_state *uts)
118{
119 struct scene *scn;
120 struct expo *exp;
121 ulong start_mem;
122 char name[100];
123 int id;
124
125 start_mem = ut_check_free();
126
127 ut_assertok(expo_new(EXPO_NAME, NULL, &exp));
128
129 scn = NULL;
130 ut_asserteq(0, exp->next_id);
131 strcpy(name, SCENE_NAME1);
132 id = scene_new(exp, name, SCENE1, &scn);
133 *name = '\0';
134 ut_assertnonnull(scn);
135 ut_asserteq(SCENE1, id);
136 ut_asserteq(SCENE1 + 1, exp->next_id);
137 ut_asserteq_ptr(exp, scn->expo);
138
139 /* Make sure the name was allocated */
140 ut_assertnonnull(scn->name);
141 ut_asserteq_str(SCENE_NAME1, scn->name);
142
143 /* Set the title */
144 strcpy(name, SCENE_TITLE);
145 ut_assertok(scene_title_set(scn, name));
146 *name = '\0';
147 ut_assertnonnull(scn->title);
148 ut_asserteq_str(SCENE_TITLE, scn->title);
149
150 /* Use an allocated ID */
151 scn = NULL;
152 id = scene_new(exp, SCENE_NAME2, 0, &scn);
153 ut_assertnonnull(scn);
154 ut_asserteq(SCENE2, id);
155 ut_asserteq(SCENE2 + 1, exp->next_id);
156 ut_asserteq_ptr(exp, scn->expo);
157
158 ut_asserteq_str(SCENE_NAME2, scn->name);
159
160 expo_destroy(exp);
161
162 ut_assertok(ut_check_delta(start_mem));
163
164 return 0;
165}
166BOOTSTD_TEST(expo_scene, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
167
168/* Check creating a scene with objects */
169static int expo_object(struct unit_test_state *uts)
170{
171 struct scene_obj_img *img;
172 struct scene_obj_txt *txt;
173 struct scene *scn;
174 struct expo *exp;
175 ulong start_mem;
176 char name[100];
177 char *data;
178 int id;
179
180 start_mem = ut_check_free();
181
182 ut_assertok(expo_new(EXPO_NAME, NULL, &exp));
183 id = scene_new(exp, SCENE_NAME1, SCENE1, &scn);
184 ut_assert(id > 0);
185
186 ut_asserteq(0, scene_obj_count(scn));
187
188 data = NULL;
189 strcpy(name, LOGO_NAME);
190 id = scene_img(scn, name, OBJ_LOGO, data, &img);
191 ut_assert(id > 0);
192 *name = '\0';
193 ut_assertnonnull(img);
194 ut_asserteq(OBJ_LOGO, id);
195 ut_asserteq(OBJ_LOGO + 1, exp->next_id);
196 ut_asserteq_ptr(scn, img->obj.scene);
197 ut_asserteq(SCENEOBJT_IMAGE, img->obj.type);
198
199 ut_asserteq_ptr(data, img->data);
200
201 /* Make sure the name was allocated */
202 ut_assertnonnull(scn->name);
203 ut_asserteq_str(SCENE_NAME1, scn->name);
204
205 ut_asserteq(1, scene_obj_count(scn));
206
207 id = scene_txt_str(scn, "text", OBJ_TEXT, STR_TEXT, "my string", &txt);
208 ut_assert(id > 0);
209 ut_assertnonnull(txt);
210 ut_asserteq(OBJ_TEXT, id);
211 ut_asserteq(SCENEOBJT_TEXT, txt->obj.type);
212 ut_asserteq(2, scene_obj_count(scn));
213
214 /* Check passing NULL as the final parameter */
215 id = scene_txt_str(scn, "text2", OBJ_TEXT2, STR_TEXT2, "another string",
216 NULL);
217 ut_assert(id > 0);
218 ut_asserteq(3, scene_obj_count(scn));
219
220 expo_destroy(exp);
221
222 ut_assertok(ut_check_delta(start_mem));
223
224 return 0;
225}
226BOOTSTD_TEST(expo_object, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
227
228/* Check setting object attributes */
229static int expo_object_attr(struct unit_test_state *uts)
230{
231 struct scene_obj_menu *menu;
232 struct scene_obj_img *img;
233 struct scene_obj_txt *txt;
234 struct scene *scn;
235 struct expo *exp;
236 ulong start_mem;
237 char name[100];
238 char *data;
239 int id;
240
241 start_mem = ut_check_free();
242
243 ut_assertok(expo_new(EXPO_NAME, NULL, &exp));
244 id = scene_new(exp, SCENE_NAME1, SCENE1, &scn);
245 ut_assert(id > 0);
246
247 data = NULL;
248 id = scene_img(scn, LOGO_NAME, OBJ_LOGO, data, &img);
249 ut_assert(id > 0);
250
251 ut_assertok(scene_obj_set_pos(scn, OBJ_LOGO, 123, 456));
252 ut_asserteq(123, img->obj.x);
253 ut_asserteq(456, img->obj.y);
254
255 ut_asserteq(-ENOENT, scene_obj_set_pos(scn, OBJ_TEXT2, 0, 0));
256
257 id = scene_txt_str(scn, "text", OBJ_TEXT, STR_TEXT, "my string", &txt);
258 ut_assert(id > 0);
259
260 strcpy(name, "font2");
261 ut_assertok(scene_txt_set_font(scn, OBJ_TEXT, name, 42));
262 ut_asserteq_ptr(name, txt->font_name);
263 ut_asserteq(42, txt->font_size);
264
265 ut_asserteq(-ENOENT, scene_txt_set_font(scn, OBJ_TEXT2, name, 42));
266
267 id = scene_menu(scn, "main", OBJ_MENU, &menu);
268 ut_assert(id > 0);
269
270 ut_assertok(scene_menu_set_title(scn, OBJ_MENU, OBJ_TEXT));
271
272 ut_asserteq(-ENOENT, scene_menu_set_title(scn, OBJ_TEXT2, OBJ_TEXT));
273 ut_asserteq(-EINVAL, scene_menu_set_title(scn, OBJ_MENU, OBJ_TEXT2));
274
275 expo_destroy(exp);
276
277 ut_assertok(ut_check_delta(start_mem));
278
279 return 0;
280}
281BOOTSTD_TEST(expo_object_attr, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
282
283/* Check creating a scene with a menu */
284static int expo_object_menu(struct unit_test_state *uts)
285{
286 struct scene_obj_menu *menu;
287 struct scene_menitem *item;
288 int id, label_id, desc_id, key_id, pointer_id, preview_id;
289 struct scene_obj_txt *ptr, *name1, *desc1, *key1, *tit, *prev1;
290 struct scene *scn;
291 struct expo *exp;
292 ulong start_mem;
293
294 start_mem = ut_check_free();
295
296 ut_assertok(expo_new(EXPO_NAME, NULL, &exp));
297 id = scene_new(exp, SCENE_NAME1, SCENE1, &scn);
298 ut_assert(id > 0);
299
300 id = scene_menu(scn, "main", OBJ_MENU, &menu);
301 ut_assert(id > 0);
302 ut_assertnonnull(menu);
303 ut_asserteq(OBJ_MENU, id);
304 ut_asserteq(SCENEOBJT_MENU, menu->obj.type);
305 ut_asserteq(0, menu->title_id);
306 ut_asserteq(0, menu->pointer_id);
307
308 ut_assertok(scene_obj_set_pos(scn, OBJ_MENU, 50, 400));
309 ut_asserteq(50, menu->obj.x);
310 ut_asserteq(400, menu->obj.y);
311
312 id = scene_txt_str(scn, "title", OBJ_MENU_TITLE, STR_MENU_TITLE,
313 "Main Menu", &tit);
314 ut_assert(id > 0);
315 ut_assertok(scene_menu_set_title(scn, OBJ_MENU, OBJ_MENU_TITLE));
316 ut_asserteq(OBJ_MENU_TITLE, menu->title_id);
317
318 pointer_id = scene_txt_str(scn, "cur_item", POINTER_TEXT,
319 STR_POINTER_TEXT, ">", &ptr);
320 ut_assert(pointer_id > 0);
321
322 ut_assertok(scene_menu_set_pointer(scn, OBJ_MENU, POINTER_TEXT));
323 ut_asserteq(POINTER_TEXT, menu->pointer_id);
324
325 label_id = scene_txt_str(scn, "label1", ITEM1_LABEL, STR_ITEM1_LABEL,
326 "Play", &name1);
327 ut_assert(label_id > 0);
328
329 desc_id = scene_txt_str(scn, "desc1", ITEM1_DESC, STR_ITEM1_DESC,
330 "Lord Melchett", &desc1);
331 ut_assert(desc_id > 0);
332
333 key_id = scene_txt_str(scn, "item1-key", ITEM1_KEY, STR_ITEM1_KEY, "1",
334 &key1);
335 ut_assert(key_id > 0);
336
337 preview_id = scene_txt_str(scn, "item1-preview", ITEM1_PREVIEW,
338 STR_ITEM1_PREVIEW, "(preview1)", &prev1);
339 ut_assert(preview_id > 0);
340
341 id = scene_menuitem(scn, OBJ_MENU, "linux", ITEM1, ITEM1_KEY,
342 ITEM1_LABEL, ITEM1_DESC, ITEM1_PREVIEW, 0, &item);
343 ut_asserteq(ITEM1, id);
344 ut_asserteq(id, item->id);
345 ut_asserteq(key_id, item->key_id);
346 ut_asserteq(label_id, item->label_id);
347 ut_asserteq(desc_id, item->desc_id);
348 ut_asserteq(preview_id, item->preview_id);
349
350 /* adding an item should cause the first item to become current */
351 ut_asserteq(id, menu->cur_item_id);
352
353 /* the title should be at the top */
354 ut_asserteq(menu->obj.x, tit->obj.x);
355 ut_asserteq(menu->obj.y, tit->obj.y);
356
357 /* the first item should be next */
358 ut_asserteq(menu->obj.x, name1->obj.x);
359 ut_asserteq(menu->obj.y + 32, name1->obj.y);
360
361 ut_asserteq(menu->obj.x + 230, key1->obj.x);
362 ut_asserteq(menu->obj.y + 32, key1->obj.y);
363
364 ut_asserteq(menu->obj.x + 200, ptr->obj.x);
365 ut_asserteq(menu->obj.y + 32, ptr->obj.y);
366
367 ut_asserteq(menu->obj.x + 280, desc1->obj.x);
368 ut_asserteq(menu->obj.y + 32, desc1->obj.y);
369
370 ut_asserteq(-4, prev1->obj.x);
371 ut_asserteq(menu->obj.y + 32, prev1->obj.y);
372 ut_asserteq(false, prev1->obj.hide);
373
374 expo_destroy(exp);
375
376 ut_assertok(ut_check_delta(start_mem));
377
378 return 0;
379}
380BOOTSTD_TEST(expo_object_menu, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
381
382/* Check rendering a scene */
383static int expo_render_image(struct unit_test_state *uts)
384{
385 struct scene_obj_menu *menu;
386 struct scene *scn, *scn2;
387 struct expo_action act;
388 struct scene_obj *obj;
389 struct udevice *dev;
390 struct expo *exp;
391 int id;
392
393 console_record_reset_enable();
394 ut_assertok(uclass_first_device_err(UCLASS_VIDEO, &dev));
395
396 ut_assertok(expo_new(EXPO_NAME, NULL, &exp));
397 id = scene_new(exp, SCENE_NAME1, SCENE1, &scn);
398 ut_assert(id > 0);
399 ut_assertok(expo_set_display(exp, dev));
400
401 id = scene_img(scn, "logo", OBJ_LOGO, video_get_u_boot_logo(), NULL);
402 ut_assert(id > 0);
403 ut_assertok(scene_obj_set_pos(scn, OBJ_LOGO, 50, 20));
404
405 id = scene_txt_str(scn, "text", OBJ_TEXT, STR_TEXT, "my string", NULL);
406 ut_assert(id > 0);
407 ut_assertok(scene_txt_set_font(scn, OBJ_TEXT, "cantoraone_regular",
408 40));
409 ut_assertok(scene_obj_set_pos(scn, OBJ_TEXT, 400, 100));
410
411 id = scene_txt_str(scn, "text", OBJ_TEXT2, STR_TEXT2, "another string",
412 NULL);
413 ut_assert(id > 0);
414 ut_assertok(scene_txt_set_font(scn, OBJ_TEXT2, "nimbus_sans_l_regular",
415 60));
416 ut_assertok(scene_obj_set_pos(scn, OBJ_TEXT2, 200, 600));
417
418 id = scene_menu(scn, "main", OBJ_MENU, &menu);
419 ut_assert(id > 0);
420
421 id = scene_txt_str(scn, "title", OBJ_MENU_TITLE, STR_MENU_TITLE,
422 "Main Menu", NULL);
423 ut_assert(id > 0);
424 ut_assertok(scene_menu_set_title(scn, OBJ_MENU, OBJ_MENU_TITLE));
425
426 id = scene_txt_str(scn, "cur_item", POINTER_TEXT, STR_POINTER_TEXT, ">",
427 NULL);
428 ut_assert(id > 0);
429 ut_assertok(scene_menu_set_pointer(scn, OBJ_MENU, POINTER_TEXT));
430
431 id = scene_txt_str(scn, "label1", ITEM1_LABEL, STR_ITEM1_LABEL, "Play",
432 NULL);
433 ut_assert(id > 0);
434 id = scene_txt_str(scn, "item1 txt", ITEM1_DESC, STR_ITEM1_DESC,
435 "Lord Melchett", NULL);
436 ut_assert(id > 0);
437 id = scene_txt_str(scn, "item1-key", ITEM1_KEY, STR_ITEM1_KEY, "1",
438 NULL);
439 ut_assert(id > 0);
440 id = scene_img(scn, "item1-preview", ITEM1_PREVIEW,
441 video_get_u_boot_logo(), NULL);
442 id = scene_menuitem(scn, OBJ_MENU, "item1", ITEM1, ITEM1_KEY,
443 ITEM1_LABEL, ITEM1_DESC, ITEM1_PREVIEW, 0, NULL);
444 ut_assert(id > 0);
445
446 id = scene_txt_str(scn, "label2", ITEM2_LABEL, STR_ITEM2_LABEL, "Now",
447 NULL);
448 ut_assert(id > 0);
449 id = scene_txt_str(scn, "item2 txt", ITEM2_DESC, STR_ITEM2_DESC,
450 "Lord Percy", NULL);
451 ut_assert(id > 0);
452 id = scene_txt_str(scn, "item2-key", ITEM2_KEY, STR_ITEM2_KEY, "2",
453 NULL);
454 ut_assert(id > 0);
455 id = scene_img(scn, "item2-preview", ITEM2_PREVIEW,
456 video_get_u_boot_logo(), NULL);
457 ut_assert(id > 0);
458
459 id = scene_menuitem(scn, OBJ_MENU, "item2", ITEM2, ITEM2_KEY,
460 ITEM2_LABEL, ITEM2_DESC, ITEM2_PREVIEW, 0, NULL);
461 ut_assert(id > 0);
462
463 ut_assertok(scene_obj_set_pos(scn, OBJ_MENU, 50, 400));
464
465 scn2 = expo_lookup_scene_id(exp, SCENE1);
466 ut_asserteq_ptr(scn, scn2);
467 scn2 = expo_lookup_scene_id(exp, SCENE2);
468 ut_assertnull(scn2);
469
470 /* render without a scene */
471 ut_asserteq(-ECHILD, expo_render(exp));
472
473 /* render it */
474 expo_set_scene_id(exp, SCENE1);
475 ut_assertok(expo_render(exp));
476
477 /* move down */
478 ut_assertok(expo_send_key(exp, BKEY_DOWN));
479
480 ut_assertok(expo_action_get(exp, &act));
481
482 ut_asserteq(EXPOACT_POINT, act.type);
483 ut_asserteq(ITEM2, act.select.id);
484 ut_assertok(expo_render(exp));
485
486 /* make sure only the preview for the second item is shown */
487 obj = scene_obj_find(scn, ITEM1_PREVIEW, SCENEOBJT_NONE);
488 ut_asserteq(true, obj->hide);
489
490 obj = scene_obj_find(scn, ITEM2_PREVIEW, SCENEOBJT_NONE);
491 ut_asserteq(false, obj->hide);
492
493 /* select it */
494 ut_assertok(expo_send_key(exp, BKEY_SELECT));
495
496 ut_assertok(expo_action_get(exp, &act));
497 ut_asserteq(EXPOACT_SELECT, act.type);
498 ut_asserteq(ITEM2, act.select.id);
499
500 /* make sure the action doesn't come again */
501 ut_asserteq(-EAGAIN, expo_action_get(exp, &act));
502
503 /* make sure there was no console output */
504 ut_assert_console_end();
505
506 /* now try in text mode */
507 exp_set_text_mode(exp, true);
508 ut_assertok(expo_render(exp));
509
510 ut_assert_nextline("U-Boot : Boot Menu");
511 ut_assert_nextline("%s", "");
512 ut_assert_nextline("Main Menu");
513 ut_assert_nextline("%s", "");
514 ut_assert_nextline(" 1 Play Lord Melchett");
515 ut_assert_nextline(" > 2 Now Lord Percy");
516
517 /* Move back up to the first item */
518 ut_assertok(expo_send_key(exp, BKEY_UP));
519
520 ut_assertok(expo_action_get(exp, &act));
521
522 ut_asserteq(EXPOACT_POINT, act.type);
523 ut_asserteq(ITEM1, act.select.id);
524
525 ut_assertok(expo_render(exp));
526 ut_assert_nextline("U-Boot : Boot Menu");
527 ut_assert_nextline("%s", "");
528 ut_assert_nextline("Main Menu");
529 ut_assert_nextline("%s", "");
530 ut_assert_nextline(" > 1 Play Lord Melchett");
531 ut_assert_nextline(" 2 Now Lord Percy");
532
533 ut_assert_console_end();
534
535 expo_destroy(exp);
536
537 return 0;
538}
539BOOTSTD_TEST(expo_render_image, UT_TESTF_DM | UT_TESTF_SCAN_FDT);