1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright 2022 Google LLC
4  * Written by Simon Glass <sjg@chromium.org>
5  */
6 
7 #include <command.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/ut.h>
14 #include <test/video.h>
15 #include "bootstd_common.h"
16 #include <test/cedit-test.h>
17 #include "../../boot/scene_internal.h"
18 
19 enum {
20 	/* scenes */
21 	SCENE1		= 7,
22 	SCENE2,
23 
24 	/* objects */
25 	OBJ_LOGO,
26 	OBJ_TEXT,
27 	OBJ_TEXT2,
28 	OBJ_TEXT3,
29 	OBJ_MENU,
30 	OBJ_MENU_TITLE,
31 	OBJ_BOX,
32 	OBJ_BOX2,
33 	OBJ_TEXTED,
34 
35 	/* strings */
36 	STR_SCENE_TITLE,
37 
38 	STR_TEXT,
39 	STR_TEXT2,
40 	STR_TEXT3,
41 	STR_TEXTED,
42 	STR_MENU_TITLE,
43 	STR_POINTER_TEXT,
44 
45 	STR_ITEM1_LABEL,
46 	STR_ITEM1_DESC,
47 	STR_ITEM1_KEY,
48 	STR_ITEM1_PREVIEW,
49 
50 	STR_ITEM2_LABEL,
51 	STR_ITEM2_DESC,
52 	STR_ITEM2_KEY,
53 	STR_ITEM2_PREVIEW,
54 
55 	/* menu items */
56 	ITEM1,
57 	ITEM1_LABEL,
58 	ITEM1_DESC,
59 	ITEM1_KEY,
60 	ITEM1_PREVIEW,
61 
62 	ITEM2,
63 	ITEM2_LABEL,
64 	ITEM2_DESC,
65 	ITEM2_KEY,
66 	ITEM2_PREVIEW,
67 
68 	/* pointer to current item */
69 	POINTER_TEXT,
70 };
71 
72 #define BAD_POINTER	((void *)1)
73 
74 /* names for various things */
75 #define EXPO_NAME	"my menus"
76 #define SCENE_NAME1	"main"
77 #define SCENE_NAME2	"second"
78 #define SCENE_TITLE	"Main Menu"
79 #define LOGO_NAME	"logo"
80 
81 /* Check base expo support */
expo_base(struct unit_test_state * uts)82 static int expo_base(struct unit_test_state *uts)
83 {
84 	struct udevice *dev;
85 	struct expo *exp;
86 	ulong start_mem;
87 	char name[100];
88 	int i;
89 
90 	ut_assertok(uclass_first_device_err(UCLASS_VIDEO, &dev));
91 
92 	start_mem = ut_check_free();
93 
94 	exp = NULL;
95 	strcpy(name, EXPO_NAME);
96 	ut_assertok(expo_new(name, NULL, &exp));
97 	*name = '\0';
98 	ut_assertnonnull(exp);
99 	ut_asserteq(0, exp->scene_id);
100 	ut_asserteq(EXPOID_BASE_ID, exp->next_id);
101 
102 	/* Make sure the name was allocated */
103 	ut_assertnonnull(exp->name);
104 	ut_asserteq_str(EXPO_NAME, exp->name);
105 
106 	ut_assertok(expo_set_display(exp, dev));
107 	expo_destroy(exp);
108 	ut_assertok(ut_check_delta(start_mem));
109 
110 	/* test handling out-of-memory conditions */
111 	for (i = 0; i < 2; i++) {
112 		struct expo *exp2;
113 
114 		malloc_enable_testing(i);
115 		exp2 = BAD_POINTER;
116 		ut_asserteq(-ENOMEM, expo_new(EXPO_NAME, NULL, &exp2));
117 		ut_asserteq_ptr(BAD_POINTER, exp2);
118 		malloc_disable_testing();
119 	}
120 
121 	return 0;
122 }
123 BOOTSTD_TEST(expo_base, UTF_DM | UTF_SCAN_FDT);
124 
125 /* Check creating a scene */
expo_scene(struct unit_test_state * uts)126 static int expo_scene(struct unit_test_state *uts)
127 {
128 	struct scene *scn;
129 	struct expo *exp;
130 	ulong start_mem;
131 	char name[100];
132 	int id, title_id;
133 
134 	start_mem = ut_check_free();
135 
136 	ut_assertok(expo_new(EXPO_NAME, NULL, &exp));
137 
138 	scn = NULL;
139 	ut_asserteq(EXPOID_BASE_ID, exp->next_id);
140 	strcpy(name, SCENE_NAME1);
141 	id = scene_new(exp, name, SCENE1, &scn);
142 	*name = '\0';
143 	ut_assertnonnull(scn);
144 	ut_asserteq(SCENE1, id);
145 	ut_asserteq(SCENE1 + 1, exp->next_id);
146 	ut_asserteq_ptr(exp, scn->expo);
147 
148 	/* Make sure the name was allocated */
149 	ut_assertnonnull(scn->name);
150 	ut_asserteq_str(SCENE_NAME1, scn->name);
151 
152 	/* Set the title */
153 	title_id = expo_str(exp, "title", STR_SCENE_TITLE, SCENE_TITLE);
154 	ut_assert(title_id >= 0);
155 
156 	/* Use an allocated ID - this will be allocated after the title str */
157 	scn = NULL;
158 	id = scene_new(exp, SCENE_NAME2, 0, &scn);
159 	ut_assertnonnull(scn);
160 	scn->title_id = title_id;
161 	ut_asserteq(STR_SCENE_TITLE + 1, id);
162 	ut_asserteq(STR_SCENE_TITLE + 2, exp->next_id);
163 	ut_asserteq_ptr(exp, scn->expo);
164 
165 	ut_asserteq_str(SCENE_NAME2, scn->name);
166 	ut_asserteq(title_id, scn->title_id);
167 
168 	expo_destroy(exp);
169 
170 	ut_assertok(ut_check_delta(start_mem));
171 
172 	return 0;
173 }
174 BOOTSTD_TEST(expo_scene, UTF_DM | UTF_SCAN_FDT);
175 
176 /* Check creating a scene with no ID */
expo_scene_no_id(struct unit_test_state * uts)177 static int expo_scene_no_id(struct unit_test_state *uts)
178 {
179 	struct scene *scn;
180 	struct expo *exp;
181 	char name[100];
182 	int id;
183 
184 	ut_assertok(expo_new(EXPO_NAME, NULL, &exp));
185 	ut_asserteq(EXPOID_BASE_ID, exp->next_id);
186 
187 	strcpy(name, SCENE_NAME1);
188 	id = scene_new(exp, SCENE_NAME1, 0, &scn);
189 	ut_asserteq(EXPOID_BASE_ID, scn->id);
190 
191 	return 0;
192 }
193 BOOTSTD_TEST(expo_scene_no_id, UTF_DM | UTF_SCAN_FDT);
194 
195 /* Check creating a scene with objects */
expo_object(struct unit_test_state * uts)196 static int expo_object(struct unit_test_state *uts)
197 {
198 	struct scene_obj_img *img;
199 	struct scene_obj_txt *txt;
200 	struct scene *scn;
201 	struct expo *exp;
202 	ulong start_mem;
203 	char name[100];
204 	char *data;
205 	int id;
206 
207 	start_mem = ut_check_free();
208 
209 	ut_assertok(expo_new(EXPO_NAME, NULL, &exp));
210 	id = scene_new(exp, SCENE_NAME1, SCENE1, &scn);
211 	ut_assert(id > 0);
212 
213 	ut_asserteq(0, scene_obj_count(scn));
214 
215 	data = NULL;
216 	strcpy(name, LOGO_NAME);
217 	id = scene_img(scn, name, OBJ_LOGO, data, &img);
218 	ut_assert(id > 0);
219 	*name = '\0';
220 	ut_assertnonnull(img);
221 	ut_asserteq(OBJ_LOGO, id);
222 	ut_asserteq(OBJ_LOGO + 1, exp->next_id);
223 	ut_asserteq_ptr(scn, img->obj.scene);
224 	ut_asserteq(SCENEOBJT_IMAGE, img->obj.type);
225 
226 	ut_asserteq_ptr(data, img->data);
227 
228 	/* Make sure the name was allocated */
229 	ut_assertnonnull(scn->name);
230 	ut_asserteq_str(SCENE_NAME1, scn->name);
231 
232 	ut_asserteq(1, scene_obj_count(scn));
233 
234 	id = scene_txt_str(scn, "text", OBJ_TEXT, STR_TEXT, "my string", &txt);
235 	ut_assert(id > 0);
236 	ut_assertnonnull(txt);
237 	ut_asserteq(OBJ_TEXT, id);
238 	ut_asserteq(SCENEOBJT_TEXT, txt->obj.type);
239 	ut_asserteq(2, scene_obj_count(scn));
240 
241 	/* Check passing NULL as the final parameter */
242 	id = scene_txt_str(scn, "text2", OBJ_TEXT2, STR_TEXT2, "another string",
243 			   NULL);
244 	ut_assert(id > 0);
245 	ut_asserteq(3, scene_obj_count(scn));
246 
247 	expo_destroy(exp);
248 
249 	ut_assertok(ut_check_delta(start_mem));
250 
251 	return 0;
252 }
253 BOOTSTD_TEST(expo_object, UTF_DM | UTF_SCAN_FDT);
254 
255 /* Check setting object attributes and using themes */
expo_object_attr(struct unit_test_state * uts)256 static int expo_object_attr(struct unit_test_state *uts)
257 {
258 	struct scene_obj_menu *menu;
259 	struct scene_obj_img *img;
260 	struct scene_obj_txt *txt;
261 	struct scene *scn;
262 	struct expo *exp;
263 	ulong start_mem;
264 	char name[100];
265 	ofnode node;
266 	char *data;
267 	int id;
268 
269 	start_mem = ut_check_free();
270 
271 	ut_assertok(expo_new(EXPO_NAME, NULL, &exp));
272 	id = scene_new(exp, SCENE_NAME1, SCENE1, &scn);
273 	ut_assert(id > 0);
274 
275 	data = NULL;
276 	id = scene_img(scn, LOGO_NAME, OBJ_LOGO, data, &img);
277 	ut_assert(id > 0);
278 
279 	ut_assertok(scene_obj_set_pos(scn, OBJ_LOGO, 123, 456));
280 	ut_asserteq(123, img->obj.bbox.x0);
281 	ut_asserteq(456, img->obj.bbox.y0);
282 
283 	ut_asserteq(-ENOENT, scene_obj_set_pos(scn, OBJ_TEXT2, 0, 0));
284 
285 	id = scene_txt_str(scn, "text", OBJ_TEXT, STR_TEXT, "my string", &txt);
286 	ut_assert(id > 0);
287 
288 	strcpy(name, "font2");
289 	ut_assertok(scene_txt_set_font(scn, OBJ_TEXT, name, 42));
290 	ut_asserteq_ptr(name, txt->gen.font_name);
291 	ut_asserteq(42, txt->gen.font_size);
292 
293 	ut_asserteq(-ENOENT, scene_txt_set_font(scn, OBJ_TEXT2, name, 42));
294 
295 	id = scene_menu(scn, "main", OBJ_MENU, &menu);
296 	ut_assert(id > 0);
297 
298 	ut_assertok(scene_menu_set_title(scn, OBJ_MENU, OBJ_TEXT));
299 
300 	ut_asserteq(-ENOENT, scene_menu_set_title(scn, OBJ_TEXT2, OBJ_TEXT));
301 	ut_asserteq(-EINVAL, scene_menu_set_title(scn, OBJ_MENU, OBJ_TEXT2));
302 
303 	node = ofnode_path("/bootstd/theme");
304 	ut_assert(ofnode_valid(node));
305 	ut_assertok(expo_apply_theme(exp, node));
306 	ut_asserteq(30, txt->gen.font_size);
307 
308 	expo_destroy(exp);
309 
310 	ut_assertok(ut_check_delta(start_mem));
311 
312 	return 0;
313 }
314 BOOTSTD_TEST(expo_object_attr, UTF_DM | UTF_SCAN_FDT);
315 
316 /**
317  * struct test_iter_priv - private data for expo-iterator test
318  *
319  * @count: number of scene objects
320  * @menu_count: number of menus
321  * @fail_at: item ID at which to return an error
322  */
323 struct test_iter_priv {
324 	int count;
325 	int menu_count;
326 	int fail_at;
327 };
328 
h_test_iter(struct scene_obj * obj,void * vpriv)329 int h_test_iter(struct scene_obj *obj, void *vpriv)
330 {
331 	struct test_iter_priv *priv = vpriv;
332 
333 	if (priv->fail_at == obj->id)
334 		return -EINVAL;
335 
336 	priv->count++;
337 	if (obj->type == SCENEOBJT_MENU)
338 		priv->menu_count++;
339 
340 	return 0;
341 }
342 
343 /* Check creating a scene with a menu */
expo_object_menu(struct unit_test_state * uts)344 static int expo_object_menu(struct unit_test_state *uts)
345 {
346 	struct scene_obj_menu *menu;
347 	struct scene_menitem *item;
348 	int id, label_id, desc_id, key_id, pointer_id, preview_id;
349 	struct scene_obj_txt *ptr, *name1, *desc1, *key1, *tit, *prev1;
350 	struct test_iter_priv priv;
351 	struct scene *scn;
352 	struct expo *exp;
353 	ulong start_mem;
354 
355 	start_mem = ut_check_free();
356 
357 	ut_assertok(expo_new(EXPO_NAME, NULL, &exp));
358 	id = scene_new(exp, SCENE_NAME1, SCENE1, &scn);
359 	ut_assert(id > 0);
360 
361 	id = scene_menu(scn, "main", OBJ_MENU, &menu);
362 	ut_assert(id > 0);
363 	ut_assertnonnull(menu);
364 	ut_asserteq(OBJ_MENU, id);
365 	ut_asserteq(SCENEOBJT_MENU, menu->obj.type);
366 	ut_asserteq(0, menu->title_id);
367 	ut_asserteq(0, menu->pointer_id);
368 
369 	ut_assertok(scene_obj_set_pos(scn, OBJ_MENU, 50, 400));
370 	ut_asserteq(50, menu->obj.bbox.x0);
371 	ut_asserteq(400, menu->obj.bbox.y0);
372 
373 	id = scene_txt_str(scn, "title", OBJ_MENU_TITLE, STR_MENU_TITLE,
374 			   "Main Menu", &tit);
375 	ut_assert(id > 0);
376 	ut_assertok(scene_menu_set_title(scn, OBJ_MENU, OBJ_MENU_TITLE));
377 	ut_asserteq(OBJ_MENU_TITLE, menu->title_id);
378 
379 	pointer_id = scene_txt_str(scn, "cur_item", POINTER_TEXT,
380 				   STR_POINTER_TEXT, ">", &ptr);
381 	ut_assert(pointer_id > 0);
382 
383 	ut_assertok(scene_menu_set_pointer(scn, OBJ_MENU, POINTER_TEXT));
384 	ut_asserteq(POINTER_TEXT, menu->pointer_id);
385 
386 	label_id = scene_txt_str(scn, "label1", ITEM1_LABEL, STR_ITEM1_LABEL,
387 				 "Play", &name1);
388 	ut_assert(label_id > 0);
389 
390 	desc_id = scene_txt_str(scn, "desc1", ITEM1_DESC, STR_ITEM1_DESC,
391 				"Lord Melchett", &desc1);
392 	ut_assert(desc_id > 0);
393 
394 	key_id = scene_txt_str(scn, "item1-key", ITEM1_KEY, STR_ITEM1_KEY, "1",
395 			       &key1);
396 	ut_assert(key_id > 0);
397 
398 	preview_id = scene_txt_str(scn, "item1-preview", ITEM1_PREVIEW,
399 				   STR_ITEM1_PREVIEW, "(preview1)", &prev1);
400 	ut_assert(preview_id > 0);
401 
402 	id = scene_menuitem(scn, OBJ_MENU, "linux", ITEM1, ITEM1_KEY,
403 			    ITEM1_LABEL, ITEM1_DESC, ITEM1_PREVIEW, 0, &item);
404 	ut_asserteq(ITEM1, id);
405 	ut_asserteq(id, item->id);
406 	ut_asserteq(key_id, item->key_id);
407 	ut_asserteq(label_id, item->label_id);
408 	ut_asserteq(desc_id, item->desc_id);
409 	ut_asserteq(preview_id, item->preview_id);
410 
411 	ut_assertok(scene_arrange(scn));
412 
413 	/* arranging the scene should cause the first item to become current */
414 	ut_asserteq(id, menu->cur_item_id);
415 
416 	/* the title should be at the top */
417 	ut_asserteq(menu->obj.bbox.x0, tit->obj.bbox.x0);
418 	ut_asserteq(menu->obj.bbox.y0, tit->obj.bbox.y0);
419 
420 	/* the first item should be next */
421 	ut_asserteq(menu->obj.bbox.x0, name1->obj.bbox.x0);
422 	ut_asserteq(menu->obj.bbox.y0 + 32, name1->obj.bbox.y0);
423 
424 	ut_asserteq(menu->obj.bbox.x0 + 230, key1->obj.bbox.x0);
425 	ut_asserteq(menu->obj.bbox.y0 + 32, key1->obj.bbox.y0);
426 
427 	ut_asserteq(menu->obj.bbox.x0 + 200, ptr->obj.bbox.x0);
428 	ut_asserteq(menu->obj.bbox.y0 + 32, ptr->obj.bbox.y0);
429 
430 	ut_asserteq(menu->obj.bbox.x0 + 280, desc1->obj.bbox.x0);
431 	ut_asserteq(menu->obj.bbox.y0 + 32, desc1->obj.bbox.y0);
432 
433 	ut_asserteq(-4, prev1->obj.bbox.x0);
434 	ut_asserteq(menu->obj.bbox.y0 + 32, prev1->obj.bbox.y0);
435 	ut_asserteq(true, prev1->obj.flags & SCENEOF_HIDE);
436 
437 	/* check iterating through scene items */
438 	memset(&priv, '\0', sizeof(priv));
439 	ut_assertok(expo_iter_scene_objs(exp, h_test_iter, &priv));
440 	ut_asserteq(7, priv.count);
441 	ut_asserteq(1, priv.menu_count);
442 
443 	/* check the iterator failing part way through iteration */
444 	memset(&priv, '\0', sizeof(priv));
445 	priv.fail_at = key_id;
446 	ut_asserteq(-EINVAL, expo_iter_scene_objs(exp, h_test_iter, &priv));
447 
448 	/* 2 items (preview_id and the menuitem) are after key_id, 7 - 2 = 5 */
449 	ut_asserteq(5, priv.count);
450 
451 	/* menu is first, so is still processed */
452 	ut_asserteq(1, priv.menu_count);
453 
454 	expo_destroy(exp);
455 
456 	ut_assertok(ut_check_delta(start_mem));
457 
458 	return 0;
459 }
460 BOOTSTD_TEST(expo_object_menu, UTF_DM | UTF_SCAN_FDT);
461 
462 /* Check rendering a scene */
expo_render_image(struct unit_test_state * uts)463 static int expo_render_image(struct unit_test_state *uts)
464 {
465 	struct scene_obj_menu *menu;
466 	struct scene *scn, *scn2;
467 	struct abuf orig, *text;
468 	struct expo_action act;
469 	struct scene_obj *obj;
470 	struct udevice *dev;
471 	struct expo *exp;
472 	int id;
473 
474 	ut_assertok(uclass_first_device_err(UCLASS_VIDEO, &dev));
475 
476 	ut_assertok(expo_new(EXPO_NAME, NULL, &exp));
477 	id = scene_new(exp, SCENE_NAME1, SCENE1, &scn);
478 	ut_assert(id > 0);
479 	ut_assertok(expo_set_display(exp, dev));
480 
481 	id = scene_img(scn, "logo", OBJ_LOGO, video_get_u_boot_logo(), NULL);
482 	ut_assert(id > 0);
483 	ut_assertok(scene_obj_set_pos(scn, OBJ_LOGO, 50, 20));
484 
485 	id = scene_txt_str(scn, "text", OBJ_TEXT, STR_TEXT, "my string", NULL);
486 	ut_assert(id > 0);
487 	ut_assertok(scene_txt_set_font(scn, OBJ_TEXT, "cantoraone_regular",
488 				       40));
489 	ut_assertok(scene_obj_set_pos(scn, OBJ_TEXT, 400, 100));
490 
491 	id = scene_txt_str(scn, "text", OBJ_TEXT2, STR_TEXT2, "another string",
492 			   NULL);
493 	ut_assert(id > 0);
494 	ut_assertok(scene_txt_set_font(scn, OBJ_TEXT2, "nimbus_sans_l_regular",
495 				       60));
496 	ut_assertok(scene_obj_set_pos(scn, OBJ_TEXT2, 200, 600));
497 
498 	/* this string is clipped as it extends beyond its bottom bound */
499 	id = scene_txt_str(scn, "text", OBJ_TEXT3, STR_TEXT3,
500 			   "this is yet\nanother string, with word-wrap and it goes on for quite a while",
501 			   NULL);
502 	ut_assert(id > 0);
503 	ut_assertok(scene_txt_set_font(scn, OBJ_TEXT3, "nimbus_sans_l_regular",
504 				       60));
505 	ut_assertok(scene_obj_set_bbox(scn, OBJ_TEXT3, 500, 200, 1000, 350));
506 
507 	id = scene_menu(scn, "main", OBJ_MENU, &menu);
508 	ut_assert(id > 0);
509 
510 	id = scene_txt_str(scn, "title", OBJ_MENU_TITLE, STR_MENU_TITLE,
511 			   "Main Menu", NULL);
512 	ut_assert(id > 0);
513 	ut_assertok(scene_menu_set_title(scn, OBJ_MENU, OBJ_MENU_TITLE));
514 
515 	id = scene_txt_str(scn, "cur_item", POINTER_TEXT, STR_POINTER_TEXT, ">",
516 			   NULL);
517 	ut_assert(id > 0);
518 	ut_assertok(scene_menu_set_pointer(scn, OBJ_MENU, POINTER_TEXT));
519 
520 	id = scene_txt_str(scn, "label1", ITEM1_LABEL, STR_ITEM1_LABEL, "Play",
521 			   NULL);
522 	ut_assert(id > 0);
523 	id = scene_txt_str(scn, "item1 txt", ITEM1_DESC, STR_ITEM1_DESC,
524 			   "Lord Melchett", NULL);
525 	ut_assert(id > 0);
526 	id = scene_txt_str(scn, "item1-key", ITEM1_KEY, STR_ITEM1_KEY, "1",
527 			   NULL);
528 	ut_assert(id > 0);
529 	id = scene_img(scn, "item1-preview", ITEM1_PREVIEW,
530 		       video_get_u_boot_logo(), NULL);
531 	id = scene_menuitem(scn, OBJ_MENU, "item1", ITEM1, ITEM1_KEY,
532 			    ITEM1_LABEL, ITEM1_DESC, ITEM1_PREVIEW, 0, NULL);
533 	ut_assert(id > 0);
534 
535 	id = scene_txt_str(scn, "label2", ITEM2_LABEL, STR_ITEM2_LABEL, "Now",
536 			   NULL);
537 	ut_assert(id > 0);
538 	id = scene_txt_str(scn, "item2 txt", ITEM2_DESC, STR_ITEM2_DESC,
539 			   "Lord Percy", NULL);
540 	ut_assert(id > 0);
541 	id = scene_txt_str(scn, "item2-key", ITEM2_KEY, STR_ITEM2_KEY, "2",
542 			   NULL);
543 	ut_assert(id > 0);
544 	id = scene_img(scn, "item2-preview", ITEM2_PREVIEW,
545 		       video_get_u_boot_logo(), NULL);
546 	ut_assert(id > 0);
547 
548 	id = scene_menuitem(scn, OBJ_MENU, "item2", ITEM2, ITEM2_KEY,
549 			    ITEM2_LABEL, ITEM2_DESC, ITEM2_PREVIEW, 0, NULL);
550 	ut_assert(id > 0);
551 
552 	ut_assertok(scene_obj_set_pos(scn, OBJ_MENU, 50, 400));
553 
554 	id = scene_box(scn, "box", OBJ_BOX, 3, NULL);
555 	ut_assert(id > 0);
556 	ut_assertok(scene_obj_set_bbox(scn, OBJ_BOX, 40, 390, 1000, 510));
557 
558 	id = scene_box(scn, "box2", OBJ_BOX2, 1, NULL);
559 	ut_assert(id > 0);
560 	ut_assertok(scene_obj_set_bbox(scn, OBJ_BOX, 500, 200, 1000, 350));
561 
562 	id = scene_texted(scn, "editor", OBJ_TEXTED, STR_TEXTED, NULL);
563 	ut_assert(id > 0);
564 	ut_assertok(scene_obj_set_bbox(scn, OBJ_TEXTED, 100, 200, 400, 650));
565 	ut_assertok(expo_edit_str(exp, STR_TEXTED, &orig, &text));
566 
567 	abuf_printf(text, "This\nis the initial contents of the text editor "
568 		"but it is quite likely that more will be added later");
569 
570 	scn2 = expo_lookup_scene_id(exp, SCENE1);
571 	ut_asserteq_ptr(scn, scn2);
572 	scn2 = expo_lookup_scene_id(exp, SCENE2);
573 	ut_assertnull(scn2);
574 
575 	/* render without a scene */
576 	ut_asserteq(-ECHILD, expo_render(exp));
577 
578 	ut_assertok(expo_calc_dims(exp));
579 	ut_assertok(scene_arrange(scn));
580 
581 	/* check dimensions of text */
582 	obj = scene_obj_find(scn, OBJ_TEXT, SCENEOBJT_NONE);
583 	ut_assertnonnull(obj);
584 	ut_asserteq(400, obj->bbox.x0);
585 	ut_asserteq(100, obj->bbox.y0);
586 	ut_asserteq(400 + 126, obj->bbox.x1);
587 	ut_asserteq(100 + 40, obj->bbox.y1);
588 
589 	/* check dimensions of image */
590 	obj = scene_obj_find(scn, OBJ_LOGO, SCENEOBJT_NONE);
591 	ut_assertnonnull(obj);
592 	ut_asserteq(50, obj->bbox.x0);
593 	ut_asserteq(20, obj->bbox.y0);
594 	ut_asserteq(50 + 160, obj->bbox.x1);
595 	ut_asserteq(20 + 160, obj->bbox.y1);
596 
597 	/* check dimensions of menu labels - both should be the same width */
598 	obj = scene_obj_find(scn, ITEM1_LABEL, SCENEOBJT_NONE);
599 	ut_assertnonnull(obj);
600 	ut_asserteq(50, obj->bbox.x0);
601 	ut_asserteq(436, obj->bbox.y0);
602 	ut_asserteq(50 + 29, obj->bbox.x1);
603 	ut_asserteq(436 + 18, obj->bbox.y1);
604 
605 	obj = scene_obj_find(scn, ITEM2_LABEL, SCENEOBJT_NONE);
606 	ut_assertnonnull(obj);
607 	ut_asserteq(50, obj->bbox.x0);
608 	ut_asserteq(454, obj->bbox.y0);
609 	ut_asserteq(50 + 29, obj->bbox.x1);
610 	ut_asserteq(454 + 18, obj->bbox.y1);
611 
612 	/* same for the key */
613 	obj = scene_obj_find(scn, ITEM1_KEY, SCENEOBJT_NONE);
614 	ut_assertnonnull(obj);
615 	ut_asserteq(280, obj->bbox.x0);
616 	ut_asserteq(436, obj->bbox.y0);
617 	ut_asserteq(280 + 9, obj->bbox.x1);
618 	ut_asserteq(436 + 18, obj->bbox.y1);
619 
620 	obj = scene_obj_find(scn, ITEM2_KEY, SCENEOBJT_NONE);
621 	ut_assertnonnull(obj);
622 	ut_asserteq(280, obj->bbox.x0);
623 	ut_asserteq(454, obj->bbox.y0);
624 	ut_asserteq(280 + 9, obj->bbox.x1);
625 	ut_asserteq(454 + 18, obj->bbox.y1);
626 
627 	/* and the description */
628 	obj = scene_obj_find(scn, ITEM1_DESC, SCENEOBJT_NONE);
629 	ut_assertnonnull(obj);
630 	ut_asserteq(330, obj->bbox.x0);
631 	ut_asserteq(436, obj->bbox.y0);
632 	ut_asserteq(330 + 89, obj->bbox.x1);
633 	ut_asserteq(436 + 18, obj->bbox.y1);
634 
635 	obj = scene_obj_find(scn, ITEM2_DESC, SCENEOBJT_NONE);
636 	ut_assertnonnull(obj);
637 	ut_asserteq(330, obj->bbox.x0);
638 	ut_asserteq(454, obj->bbox.y0);
639 	ut_asserteq(330 + 89, obj->bbox.x1);
640 	ut_asserteq(454 + 18, obj->bbox.y1);
641 
642 	/* check dimensions of menu */
643 	obj = scene_obj_find(scn, OBJ_MENU, SCENEOBJT_NONE);
644 	ut_assertnonnull(obj);
645 	ut_asserteq(50, obj->bbox.x0);
646 	ut_asserteq(400, obj->bbox.y0);
647 	ut_asserteq(50 + 160, obj->bbox.x1);
648 	ut_asserteq(400 + 160, obj->bbox.y1);
649 
650 	scene_obj_set_width(scn, OBJ_MENU, 170);
651 	ut_asserteq(50 + 170, obj->bbox.x1);
652 	scene_obj_set_bbox(scn, OBJ_MENU, 60, 410, 50 + 160, 400 + 160);
653 	ut_asserteq(60, obj->bbox.x0);
654 	ut_asserteq(410, obj->bbox.y0);
655 	ut_asserteq(50 + 160, obj->bbox.x1);
656 	ut_asserteq(400 + 160, obj->bbox.y1);
657 
658 	/* reset back to normal */
659 	scene_obj_set_bbox(scn, OBJ_MENU, 50, 400, 50 + 160, 400 + 160);
660 
661 	/* render it */
662 	expo_set_scene_id(exp, SCENE1);
663 	ut_assertok(expo_render(exp));
664 
665 	ut_asserteq(0, scn->highlight_id);
666 	ut_assertok(scene_arrange(scn));
667 	ut_asserteq(0, scn->highlight_id);
668 
669 	scene_set_highlight_id(scn, OBJ_MENU);
670 	ut_assertok(scene_arrange(scn));
671 	ut_asserteq(OBJ_MENU, scn->highlight_id);
672 	ut_assertok(expo_render(exp));
673 
674 	ut_asserteq(19704, video_compress_fb(uts, dev, false));
675 
676 	/* move down */
677 	ut_assertok(expo_send_key(exp, BKEY_DOWN));
678 
679 	ut_assertok(expo_action_get(exp, &act));
680 
681 	ut_asserteq(EXPOACT_POINT_ITEM, act.type);
682 	ut_asserteq(ITEM2, act.select.id);
683 	ut_assertok(scene_menu_select_item(scn, OBJ_MENU, act.select.id));
684 	ut_asserteq(ITEM2, scene_menu_get_cur_item(scn, OBJ_MENU));
685 	ut_assertok(scene_arrange(scn));
686 	ut_assertok(expo_render(exp));
687 	ut_asserteq(19673, video_compress_fb(uts, dev, false));
688 	ut_assertok(video_check_copy_fb(uts, dev));
689 
690 	/* hide the text editor since the following tets don't need it */
691 	scene_obj_set_hide(scn, OBJ_TEXTED, true);
692 
693 	/* do some alignment checks */
694 	ut_assertok(scene_obj_set_halign(scn, OBJ_TEXT3, SCENEOA_CENTRE));
695 	ut_assertok(expo_render(exp));
696 	ut_asserteq(16368, video_compress_fb(uts, dev, false));
697 	ut_assertok(scene_obj_set_halign(scn, OBJ_TEXT3, SCENEOA_RIGHT));
698 	ut_assertok(expo_render(exp));
699 	ut_asserteq(16321, video_compress_fb(uts, dev, false));
700 
701 	ut_assertok(scene_obj_set_halign(scn, OBJ_TEXT3, SCENEOA_LEFT));
702 	ut_assertok(scene_obj_set_valign(scn, OBJ_TEXT3, SCENEOA_CENTRE));
703 	ut_assertok(expo_render(exp));
704 	ut_asserteq(18763, video_compress_fb(uts, dev, false));
705 	ut_assertok(scene_obj_set_valign(scn, OBJ_TEXT3, SCENEOA_BOTTOM));
706 	ut_assertok(expo_render(exp));
707 	ut_asserteq(18714, video_compress_fb(uts, dev, false));
708 
709 	/* make sure only the preview for the second item is shown */
710 	obj = scene_obj_find(scn, ITEM1_PREVIEW, SCENEOBJT_NONE);
711 	ut_asserteq(true, obj->flags & SCENEOF_HIDE);
712 
713 	obj = scene_obj_find(scn, ITEM2_PREVIEW, SCENEOBJT_NONE);
714 	ut_asserteq(false, obj->flags & SCENEOF_HIDE);
715 
716 	/* select it */
717 	ut_assertok(expo_send_key(exp, BKEY_SELECT));
718 
719 	ut_assertok(expo_action_get(exp, &act));
720 	ut_asserteq(EXPOACT_SELECT, act.type);
721 	ut_asserteq(ITEM2, act.select.id);
722 
723 	/* make sure the action doesn't come again */
724 	ut_asserteq(-EAGAIN, expo_action_get(exp, &act));
725 
726 	/* make sure there was no console output */
727 	ut_assert_console_end();
728 
729 	/* now try with the highlight */
730 	exp->show_highlight = true;
731 	ut_assertok(scene_arrange(scn));
732 	ut_assertok(expo_render(exp));
733 	ut_asserteq(18844, video_compress_fb(uts, dev, false));
734 
735 	/* now try in text mode */
736 	expo_set_text_mode(exp, true);
737 	ut_assertok(expo_render(exp));
738 
739 	ut_assert_nextline("U-Boot    :    Boot Menu");
740 	ut_assert_nextline("%s", "");
741 	ut_assert_nextline("Main Menu");
742 	ut_assert_nextline("%s", "");
743 	ut_assert_nextline("       1  Play        Lord Melchett");
744 	ut_assert_nextline("  >    2  Now         Lord Percy");
745 
746 	/* Move back up to the first item */
747 	ut_assertok(expo_send_key(exp, BKEY_UP));
748 
749 	ut_assertok(expo_action_get(exp, &act));
750 
751 	ut_asserteq(EXPOACT_POINT_ITEM, act.type);
752 	ut_asserteq(ITEM1, act.select.id);
753 	ut_assertok(scene_menu_select_item(scn, OBJ_MENU, act.select.id));
754 
755 	ut_assertok(expo_render(exp));
756 	ut_assert_nextline("U-Boot    :    Boot Menu");
757 	ut_assert_nextline("%s", "");
758 	ut_assert_nextline("Main Menu");
759 	ut_assert_nextline("%s", "");
760 	ut_assert_nextline("  >    1  Play        Lord Melchett");
761 	ut_assert_nextline("       2  Now         Lord Percy");
762 
763 	ut_assert_console_end();
764 
765 	expo_destroy(exp);
766 
767 	return 0;
768 }
769 BOOTSTD_TEST(expo_render_image, UTF_DM | UTF_SCAN_FDT | UTF_CONSOLE);
770 
771 /* Check building an expo from a devicetree description */
expo_test_build(struct unit_test_state * uts)772 static int expo_test_build(struct unit_test_state *uts)
773 {
774 	struct scene_obj_menu *menu;
775 	struct scene_menitem *item;
776 	struct scene_obj_txt *txt;
777 	struct abuf orig, *copy;
778 	struct scene_obj *obj;
779 	struct scene *scn;
780 	struct expo *exp;
781 	int count;
782 	ofnode node;
783 
784 	node = ofnode_path("/cedit");
785 	ut_assert(ofnode_valid(node));
786 	ut_assertok(expo_build(node, &exp));
787 
788 	ut_asserteq_str("name", exp->name);
789 	ut_asserteq(0, exp->scene_id);
790 	ut_asserteq(ID_DYNAMIC_START + 24, exp->next_id);
791 	ut_asserteq(false, exp->popup);
792 
793 	/* check the scene */
794 	scn = expo_lookup_scene_id(exp, ID_SCENE1);
795 	ut_assertnonnull(scn);
796 	ut_asserteq_str("main", scn->name);
797 	ut_asserteq(ID_SCENE1, scn->id);
798 	ut_asserteq(ID_DYNAMIC_START, scn->title_id);
799 	ut_asserteq(0, scn->highlight_id);
800 
801 	/* check the title */
802 	txt = scene_obj_find(scn, scn->title_id, SCENEOBJT_NONE);
803 	ut_assertnonnull(txt);
804 	obj = &txt->obj;
805 	ut_asserteq_ptr(scn, obj->scene);
806 	ut_asserteq_str("title", obj->name);
807 	ut_asserteq(scn->title_id, obj->id);
808 	ut_asserteq(SCENEOBJT_TEXT, obj->type);
809 	ut_asserteq(0, obj->flags);
810 	ut_asserteq_str("Test Configuration",
811 			expo_get_str(exp, txt->gen.str_id));
812 
813 	/* check the menu */
814 	menu = scene_obj_find(scn, ID_CPU_SPEED, SCENEOBJT_NONE);
815 	obj = &menu->obj;
816 	ut_asserteq_ptr(scn, obj->scene);
817 	ut_asserteq_str("cpu-speed", obj->name);
818 	ut_asserteq(ID_CPU_SPEED, obj->id);
819 	ut_asserteq(SCENEOBJT_MENU, obj->type);
820 	ut_asserteq(0, obj->flags);
821 
822 	txt = scene_obj_find(scn, menu->title_id, SCENEOBJT_NONE);
823 	ut_asserteq_str("CPU speed", expo_get_str(exp, txt->gen.str_id));
824 
825 	ut_asserteq(0, menu->cur_item_id);
826 	ut_asserteq(0, menu->pointer_id);
827 
828 	/* check the items */
829 	item = list_first_entry(&menu->item_head, struct scene_menitem,
830 				sibling);
831 	ut_asserteq_str("00", item->name);
832 	ut_asserteq(ID_CPU_SPEED_1, item->id);
833 	ut_asserteq(0, item->key_id);
834 	ut_asserteq(0, item->desc_id);
835 	ut_asserteq(0, item->preview_id);
836 	ut_asserteq(0, item->flags);
837 	ut_asserteq(0, item->value);
838 
839 	txt = scene_obj_find(scn, item->label_id, SCENEOBJT_NONE);
840 	ut_asserteq_str("2 GHz", expo_get_str(exp, txt->gen.str_id));
841 
842 	count = list_count_nodes(&menu->item_head);
843 	ut_asserteq(3, count);
844 
845 	/* try editing some text */
846 	ut_assertok(expo_edit_str(exp, txt->gen.str_id, &orig, &copy));
847 	ut_asserteq_str("2 GHz", orig.data);
848 	ut_asserteq_str("2 GHz", copy->data);
849 
850 	/* change it and check that things look right */
851 	abuf_printf(copy, "atlantic %d", 123);
852 	ut_asserteq_str("2 GHz", orig.data);
853 	ut_asserteq_str("atlantic 123", copy->data);
854 
855 	expo_destroy(exp);
856 
857 	return 0;
858 }
859 BOOTSTD_TEST(expo_test_build, UTF_DM);
860