1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright 2023 Google LLC
4  * Written by Simon Glass <sjg@chromium.org>
5  */
6 
7 #include <cedit.h>
8 #include <dm.h>
9 #include <env.h>
10 #include <expo.h>
11 #include <mapmem.h>
12 #include <dm/ofnode.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 /* Check the cedit command */
cedit_base(struct unit_test_state * uts)20 static int cedit_base(struct unit_test_state *uts)
21 {
22 	extern struct expo *cur_exp;
23 	struct scene_obj_menu *menu;
24 	struct scene_obj_txt *txt;
25 	struct expo *exp;
26 	struct scene *scn;
27 
28 	ut_assertok(run_command("cedit load hostfs - cedit.dtb", 0));
29 
30 	/*
31 	 * ^N  Move down to second menu
32 	 * ^M  Open menu
33 	 * ^N  Move down to second item
34 	 * ^M  Select item
35 	 * \e  Quit
36 	 *
37 	 * cedit_run() returns -EACCESS so this command returns CMD_RET_FAILURE
38 	 */
39 	console_in_puts("\x0e\x0d\x0e\x0d\e");
40 	ut_asserteq(1, run_command("cedit run", 0));
41 
42 	exp = cur_exp;
43 	scn = expo_lookup_scene_id(exp, exp->scene_id);
44 	ut_assertnonnull(scn);
45 
46 	menu = scene_obj_find(scn, scn->highlight_id, SCENEOBJT_NONE);
47 	ut_assertnonnull(menu);
48 
49 	txt = scene_obj_find(scn, menu->title_id, SCENEOBJT_NONE);
50 	ut_assertnonnull(txt);
51 	ut_asserteq_str("AC Power", expo_get_str(exp, txt->gen.str_id));
52 
53 	ut_asserteq(ID_AC_ON, menu->cur_item_id);
54 
55 	return 0;
56 }
57 BOOTSTD_TEST(cedit_base, UTF_CONSOLE);
58 
59 /* Check the cedit write_fdt and read_fdt commands */
cedit_fdt(struct unit_test_state * uts)60 static int cedit_fdt(struct unit_test_state *uts)
61 {
62 	struct scene_obj_textline *tline;
63 	struct video_priv *vid_priv;
64 	extern struct expo *cur_exp;
65 	struct scene_obj_menu *menu;
66 	struct udevice *dev;
67 	ulong addr = 0x1000;
68 	struct ofprop prop;
69 	struct scene *scn;
70 	oftree tree;
71 	ofnode node;
72 	char *str;
73 	void *fdt;
74 	int i;
75 
76 	ut_assertok(uclass_first_device_err(UCLASS_VIDEO, &dev));
77 	vid_priv = dev_get_uclass_priv(dev);
78 
79 	ut_assertok(run_command("cedit load hostfs - cedit.dtb", 0));
80 
81 	ut_asserteq(ID_SCENE1, cedit_prepare(cur_exp, dev, &scn));
82 
83 	/* get a menu to fiddle with */
84 	menu = scene_obj_find(scn, ID_CPU_SPEED, SCENEOBJT_MENU);
85 	ut_assertnonnull(menu);
86 	menu->cur_item_id = ID_CPU_SPEED_2;
87 
88 	/* get a textline to fiddle with too */
89 	tline = scene_obj_find(scn, ID_MACHINE_NAME, SCENEOBJT_TEXTLINE);
90 	ut_assertnonnull(tline);
91 	str = abuf_data(&tline->buf);
92 	strcpy(str, "my-machine");
93 
94 	ut_assertok(run_command("cedit write_fdt hostfs - settings.dtb", 0));
95 	ut_assertok(run_commandf("load hostfs - %lx settings.dtb", addr));
96 	ut_assert_nextlinen("1024 bytes read");
97 
98 	fdt = map_sysmem(addr, 1024);
99 	tree = oftree_from_fdt(fdt);
100 	node = ofnode_find_subnode(oftree_root(tree), CEDIT_NODE_NAME);
101 	ut_assert(ofnode_valid(node));
102 
103 	ut_asserteq(ID_CPU_SPEED_2,
104 		    ofnode_read_u32_default(node, "cpu-speed", 0));
105 	ut_asserteq(3,
106 		    ofnode_read_u32_default(node, "cpu-speed-value", 0));
107 	ut_asserteq_str("2.5 GHz", ofnode_read_string(node, "cpu-speed-str"));
108 	ut_asserteq_str("my-machine", ofnode_read_string(node, "machine-name"));
109 
110 	/* There should only be 7 properties */
111 	for (i = 0, ofnode_first_property(node, &prop); ofprop_valid(&prop);
112 	     i++, ofnode_next_property(&prop))
113 		;
114 	ut_asserteq(7, i);
115 
116 	ut_assert_console_end();
117 
118 	/* reset the expo */
119 	menu->cur_item_id = ID_CPU_SPEED_1;
120 	*str = '\0';
121 
122 	/* load in the settings and make sure they update */
123 	ut_assertok(run_command("cedit read_fdt hostfs - settings.dtb", 0));
124 	ut_asserteq(ID_CPU_SPEED_2, menu->cur_item_id);
125 	ut_asserteq_str("my-machine", ofnode_read_string(node, "machine-name"));
126 
127 	ut_assertnonnull(menu);
128 	ut_assert_console_end();
129 
130 	return 0;
131 }
132 BOOTSTD_TEST(cedit_fdt, UTF_CONSOLE);
133 
134 /* Check the cedit write_env and read_env commands */
cedit_env(struct unit_test_state * uts)135 static int cedit_env(struct unit_test_state *uts)
136 {
137 	struct scene_obj_textline *tline;
138 	struct video_priv *vid_priv;
139 	extern struct expo *cur_exp;
140 	struct scene_obj_menu *menu;
141 	struct udevice *dev;
142 	struct scene *scn;
143 	char *str;
144 
145 	ut_assertok(run_command("cedit load hostfs - cedit.dtb", 0));
146 
147 	ut_assertok(uclass_first_device_err(UCLASS_VIDEO, &dev));
148 	vid_priv = dev_get_uclass_priv(dev);
149 
150 	ut_asserteq(ID_SCENE1, cedit_prepare(cur_exp, dev, &scn));
151 
152 	/* get a menu to fiddle with */
153 	menu = scene_obj_find(scn, ID_CPU_SPEED, SCENEOBJT_MENU);
154 	ut_assertnonnull(menu);
155 	menu->cur_item_id = ID_CPU_SPEED_2;
156 
157 	/* get a textline to fiddle with too */
158 	tline = scene_obj_find(scn, ID_MACHINE_NAME, SCENEOBJT_TEXTLINE);
159 	ut_assertnonnull(tline);
160 	str = abuf_data(&tline->buf);
161 	strcpy(str, "my-machine");
162 
163 	ut_assertok(run_command("cedit write_env -v", 0));
164 	ut_assert_nextlinen("c.cpu-speed=11");
165 	ut_assert_nextlinen("c.cpu-speed-str=2.5 GHz");
166 	ut_assert_nextlinen("c.cpu-speed-value=3");
167 	ut_assert_nextlinen("c.power-loss=14");
168 	ut_assert_nextlinen("c.power-loss-str=Always Off");
169 	ut_assert_nextlinen("c.power-loss-value=0");
170 	ut_assert_nextlinen("c.machine-name=my-machine");
171 	ut_assert_console_end();
172 
173 	ut_asserteq(11, env_get_ulong("c.cpu-speed", 10, 0));
174 	ut_asserteq_str("2.5 GHz", env_get("c.cpu-speed-str"));
175 	ut_asserteq_str("my-machine", env_get("c.machine-name"));
176 
177 	/* reset the expo */
178 	menu->cur_item_id = ID_CPU_SPEED_1;
179 	*str = '\0';
180 
181 	ut_assertok(run_command("cedit read_env -v", 0));
182 	ut_assert_nextlinen("c.cpu-speed=11");
183 	ut_assert_nextlinen("c.power-loss=14");
184 	ut_assert_nextlinen("c.machine-name=my-machine");
185 	ut_assert_console_end();
186 
187 	ut_asserteq(ID_CPU_SPEED_2, menu->cur_item_id);
188 	ut_asserteq_str("my-machine", env_get("c.machine-name"));
189 
190 	return 0;
191 }
192 BOOTSTD_TEST(cedit_env, UTF_CONSOLE);
193 
194 /* Check the cedit write_cmos and read_cmos commands */
cedit_cmos(struct unit_test_state * uts)195 static int cedit_cmos(struct unit_test_state *uts)
196 {
197 	struct scene_obj_menu *menu, *menu2;
198 	struct video_priv *vid_priv;
199 	extern struct expo *cur_exp;
200 	struct udevice *dev;
201 	struct scene *scn;
202 
203 	ut_assertok(run_command("cedit load hostfs - cedit.dtb", 0));
204 
205 	ut_assertok(uclass_first_device_err(UCLASS_VIDEO, &dev));
206 	vid_priv = dev_get_uclass_priv(dev);
207 	ut_asserteq(ID_SCENE1, cedit_prepare(cur_exp, dev, &scn));
208 
209 	/* get the menus to fiddle with */
210 	menu = scene_obj_find(scn, ID_CPU_SPEED, SCENEOBJT_MENU);
211 	ut_assertnonnull(menu);
212 	menu->cur_item_id = ID_CPU_SPEED_2;
213 
214 	menu2 = scene_obj_find(scn, ID_POWER_LOSS, SCENEOBJT_MENU);
215 	ut_assertnonnull(menu2);
216 	menu2->cur_item_id = ID_AC_MEMORY;
217 
218 	ut_assertok(run_command("cedit write_cmos -v", 0));
219 	ut_assert_nextlinen("Write 2 bytes from offset 80 to 84");
220 	ut_assert_console_end();
221 
222 	/* reset the expo */
223 	menu->cur_item_id = ID_CPU_SPEED_1;
224 	menu2->cur_item_id = ID_AC_OFF;
225 
226 	ut_assertok(run_command("cedit read_cmos -v", 0));
227 	ut_assert_nextlinen("Read 2 bytes from offset 80 to 84");
228 	ut_assert_console_end();
229 
230 	ut_asserteq(ID_CPU_SPEED_2, menu->cur_item_id);
231 	ut_asserteq(ID_AC_MEMORY, menu2->cur_item_id);
232 
233 	return 0;
234 }
235 BOOTSTD_TEST(cedit_cmos, UTF_CONSOLE);
236 
237 /* Check the cedit displays correctly */
cedit_render(struct unit_test_state * uts)238 static int cedit_render(struct unit_test_state *uts)
239 {
240 	struct scene_obj_menu *menu;
241 	struct video_priv *vid_priv;
242 	extern struct expo *cur_exp;
243 	struct expo_action evt;
244 	struct expo_action act;
245 	struct udevice *dev, *con;
246 	struct stdio_dev *sdev;
247 	struct scene *scn;
248 	struct expo *exp;
249 	int i;
250 
251 	ut_assertok(run_command("cedit load hostfs - cedit.dtb", 0));
252 
253 	exp = cur_exp;
254 	sdev = stdio_get_by_name("vidconsole");
255 	ut_assertnonnull(sdev);
256 	con = sdev->priv;
257 
258 	dev = dev_get_parent(con);
259 	vid_priv = dev_get_uclass_priv(dev);
260 	ut_asserteq(ID_SCENE1, cedit_prepare(exp, dev, &scn));
261 
262 	menu = scene_obj_find(scn, ID_POWER_LOSS, SCENEOBJT_MENU);
263 	ut_assertnonnull(menu);
264 	ut_asserteq(ID_AC_OFF, menu->cur_item_id);
265 
266 	ut_assertok(expo_render(exp));
267 	ut_asserteq(4929, video_compress_fb(uts, dev, false));
268 	ut_assertok(video_check_copy_fb(uts, dev));
269 
270 	/* move to the second menu */
271 	act.type = EXPOACT_POINT_OBJ;
272 	act.select.id = ID_POWER_LOSS;
273 	ut_assertok(cedit_do_action(exp, scn, vid_priv, &act));
274 	ut_assertok(expo_render(exp));
275 	ut_asserteq(4986, video_compress_fb(uts, dev, false));
276 
277 	/* open the menu */
278 	act.type = EXPOACT_OPEN;
279 	act.select.id = ID_POWER_LOSS;
280 	ut_assertok(cedit_do_action(exp, scn, vid_priv, &act));
281 	ut_assertok(expo_render(exp));
282 	ut_asserteq(5393, video_compress_fb(uts, dev, false));
283 
284 	/* close the menu */
285 	act.type = EXPOACT_CLOSE;
286 	act.select.id = ID_POWER_LOSS;
287 	ut_assertok(cedit_do_action(exp, scn, vid_priv, &act));
288 	ut_assertok(expo_render(exp));
289 	ut_asserteq(4986, video_compress_fb(uts, dev, false));
290 
291 	/* open the menu again to check it looks the same */
292 	act.type = EXPOACT_OPEN;
293 	act.select.id = ID_POWER_LOSS;
294 	ut_assertok(cedit_do_action(exp, scn, vid_priv, &act));
295 	ut_assertok(expo_render(exp));
296 	ut_asserteq(5393, video_compress_fb(uts, dev, false));
297 
298 	/* close the menu */
299 	act.type = EXPOACT_CLOSE;
300 	act.select.id = ID_POWER_LOSS;
301 	ut_assertok(cedit_do_action(exp, scn, vid_priv, &act));
302 	ut_assertok(expo_render(exp));
303 	ut_asserteq(4986, video_compress_fb(uts, dev, false));
304 
305 	act.type = EXPOACT_OPEN;
306 	act.select.id = ID_POWER_LOSS;
307 	ut_assertok(cedit_do_action(exp, scn, vid_priv, &act));
308 	ut_assertok(expo_render(exp));
309 	ut_asserteq(5393, video_compress_fb(uts, dev, false));
310 
311 	act.type = EXPOACT_POINT_ITEM;
312 	act.select.id = ID_AC_ON;
313 	ut_assertok(cedit_do_action(exp, scn, vid_priv, &act));
314 	ut_assertok(expo_render(exp));
315 	ut_asserteq(5365, video_compress_fb(uts, dev, false));
316 
317 	/* select it */
318 	act.type = EXPOACT_SELECT;
319 	act.select.id = ID_AC_ON;
320 	ut_assertok(cedit_do_action(exp, scn, vid_priv, &act));
321 	ut_assertok(expo_render(exp));
322 	ut_asserteq(4980, video_compress_fb(uts, dev, false));
323 
324 	ut_asserteq(ID_AC_ON, menu->cur_item_id);
325 
326 	/* move to the line-edit field */
327 	act.type = EXPOACT_POINT_OBJ;
328 	act.select.id = ID_MACHINE_NAME;
329 	ut_assertok(cedit_do_action(exp, scn, vid_priv, &act));
330 	ut_assertok(expo_render(exp));
331 	ut_asserteq(4862, video_compress_fb(uts, dev, false));
332 
333 	/* open it */
334 	act.type = EXPOACT_OPEN;
335 	act.select.id = ID_MACHINE_NAME;
336 	ut_assertok(cedit_do_action(exp, scn, vid_priv, &act));
337 	ut_assertok(expo_render(exp));
338 	ut_asserteq(4851, video_compress_fb(uts, dev, false));
339 
340 	/*
341 	 * Send some keypresses. Note that the console must be enabled so that
342 	 * the characters actually reach the putc_xy() in console_truetype,
343 	 * since in scene_textline_send_key(), the lineedit restores the
344 	 * vidconsole state, outputs the character and then saves the state
345 	 * again. If the character is never output, then the state won't be
346 	 * updated and the lineedit will be inconsistent.
347 	 */
348 	ut_unsilence_console(uts);
349 	for (i = 'a'; i < 'd'; i++)
350 		ut_assertok(scene_send_key(scn, i, &evt));
351 	ut_silence_console(uts);
352 	ut_assertok(cedit_arange(exp, vid_priv, scn->id));
353 	ut_assertok(expo_render(exp));
354 	ut_asserteq(4996, video_compress_fb(uts, dev, false));
355 
356 	expo_destroy(exp);
357 	cur_exp = NULL;
358 
359 	return 0;
360 }
361 BOOTSTD_TEST(cedit_render, UTF_DM | UTF_SCAN_FDT);
362 
363 /* Check the cedit displays lineedits correctly */
cedit_render_lineedit(struct unit_test_state * uts)364 static int cedit_render_lineedit(struct unit_test_state *uts)
365 {
366 	struct scene_obj_textline *tline;
367 	struct video_priv *vid_priv;
368 	extern struct expo *cur_exp;
369 	struct expo_action evt;
370 	struct expo_action act;
371 	struct udevice *dev, *con;
372 	struct stdio_dev *sdev;
373 	struct scene *scn;
374 	struct expo *exp;
375 	char *str;
376 	int i;
377 
378 	ut_assertok(run_command("cedit load hostfs - cedit.dtb", 0));
379 
380 	exp = cur_exp;
381 	sdev = stdio_get_by_name("vidconsole");
382 	ut_assertnonnull(sdev);
383 	con = sdev->priv;
384 
385 	dev = dev_get_parent(con);
386 	vid_priv = dev_get_uclass_priv(dev);
387 	ut_asserteq(ID_SCENE1, cedit_prepare(exp, dev, &scn));
388 
389 	/* set up an initial value for the textline */
390 	tline = scene_obj_find(scn, ID_MACHINE_NAME, SCENEOBJT_TEXTLINE);
391 	ut_assertnonnull(tline);
392 	str = abuf_data(&tline->buf);
393 	strcpy(str, "my-machine");
394 	ut_asserteq(20, tline->pos);
395 
396 	ut_assertok(expo_render(exp));
397 	ut_asserteq(5336, video_compress_fb(uts, dev, false));
398 	ut_assertok(video_check_copy_fb(uts, dev));
399 
400 	/* move to the line-edit field */
401 	act.type = EXPOACT_POINT_OBJ;
402 	act.select.id = ID_MACHINE_NAME;
403 	ut_assertok(cedit_do_action(exp, scn, vid_priv, &act));
404 	ut_assertok(expo_render(exp));
405 	ut_asserteq(5363, video_compress_fb(uts, dev, false));
406 
407 	/* open it */
408 	act.type = EXPOACT_OPEN;
409 	act.select.id = ID_MACHINE_NAME;
410 	ut_assertok(cedit_do_action(exp, scn, vid_priv, &act));
411 	// ut_asserteq(0, tline->pos);
412 	ut_assertok(expo_render(exp));
413 	ut_asserteq(5283, video_compress_fb(uts, dev, false));
414 
415 	/* delete some characters */
416 	ut_unsilence_console(uts);
417 	for (i = 0; i < 3; i++)
418 		ut_assertok(scene_send_key(scn, '\b', &evt));
419 	ut_silence_console(uts);
420 	ut_asserteq_str("my-mach", str);
421 
422 	ut_assertok(cedit_arange(exp, vid_priv, scn->id));
423 	ut_assertok(expo_render(exp));
424 	ut_asserteq(5170, video_compress_fb(uts, dev, false));
425 
426 	expo_destroy(exp);
427 	cur_exp = NULL;
428 
429 	return 0;
430 }
431 BOOTSTD_TEST(cedit_render_lineedit, UTF_DM | UTF_SCAN_FDT);
432