xref: /aosp_15_r20/external/coreboot/tests/lib/ux_locales-test.c (revision b9411a12aaaa7e1e6a6fb7c5e057f44ee179a49c)
1 /* SPDX-License-Identifier: GPL-2.0-only */
2 
3 #include <cbfs.h>
4 #include <stdbool.h>
5 #include <string.h>
6 #include <tests/test.h>
7 #include <ux_locales.h>
8 #include <vb2_api.h>
9 
10 #define DATA_DEFAULT                                                                           \
11 	(                                                                                      \
12 		"\x01"  /* Version. */                                                         \
13 		"name_1\x00"  /* name_1, langs = [0, 2, 30]. */                                \
14 		"0\x00translation_1_0\x00"                                                     \
15 		"2\x00translation_1_2\x00"                                                     \
16 		"30\x00translation_1_30\x00"                                                   \
17 		"\x01"                                                                         \
18 		"name_15\x00"  /* name_15, langs = [4, 25, 60]. */                             \
19 		"4\x00translation_15_4\x00"                                                    \
20 		"25\x00translation_15_25\x00"                                                  \
21 		"60\x00translation_15_60\x00"                                                  \
22 		"\x01"                                                                         \
23 		"name_20\x00"  /* name_20, langs = [8]. */                                     \
24 		"8\x00translation_20_8\x00"                                                    \
25 		"\x01"                                                                         \
26 	)
27 const unsigned char data_default[] = DATA_DEFAULT;
28 
29 /*
30  * The data must be set in the setup function and might be used in cbfs related functions.
31  * The size of the data must be recorded because the data contains the null character \x00.
32  *
33  * Note that sizeof(DATA_DEFAULT) will count the '\0' at the end, so DATA_DEFAULT_SIZE, i.e.
34  * the real data size must be minus one.
35  */
36 #define DATA_DEFAULT_SIZE (sizeof(DATA_DEFAULT) - 1)
37 
38 #define MAX_DATA_SIZE (DATA_DEFAULT_SIZE + 1)
39 struct {
40 	unsigned char raw[MAX_DATA_SIZE];
41 	size_t size;
42 } data;
43 
44 /* Mock functions. */
cbfs_unmap(void * mapping)45 void cbfs_unmap(void *mapping)
46 {
47 	/* No-op. */
48 }
49 
50 /* We can't mock cbfs_ro_map() directly as it's a static inline function. */
_cbfs_alloc(const char * name,cbfs_allocator_t allocator,void * arg,size_t * size_out,bool force_ro,enum cbfs_type * type)51 void *_cbfs_alloc(const char *name, cbfs_allocator_t allocator, void *arg,
52 		  size_t *size_out, bool force_ro, enum cbfs_type *type)
53 {
54 	/* Mock a successful CBFS mapping. */
55 	if (mock_type(bool)) {
56 		*size_out = data.size;
57 		return data.raw;
58 	}
59 	*size_out = 0;
60 	return NULL;
61 }
62 
vb2api_get_locale_id(struct vb2_context * ctx)63 uint32_t vb2api_get_locale_id(struct vb2_context *ctx)
64 {
65 	return mock_type(uint32_t);
66 }
67 
vboot_get_context(void)68 struct vb2_context *vboot_get_context(void)
69 {
70 	static struct vb2_context vboot2_ctx;
71 	return &vboot2_ctx;
72 }
73 
74 /* Test states for test_ux_locales_get_text with valid CBFS data. */
75 struct ux_locales_test_state {
76 	const char *name;
77 	uint32_t lang_id;
78 	const char *expect;
79 };
80 
81 /* Setup and teardown functions. */
setup_default(void ** state)82 static int setup_default(void **state)
83 {
84 	void *ret;
85 	/* Setup the mocked cbfs region data. */
86 	data.size = DATA_DEFAULT_SIZE;
87 	ret = memset(data.raw, 0xff, MAX_DATA_SIZE);
88 	if (!ret)
89 		return 1;
90 	ret = memcpy(data.raw, data_default, data.size);
91 	if (!ret)
92 		return 1;
93 	return 0;
94 }
95 
setup_bad_version(void ** state)96 static int setup_bad_version(void **state)
97 {
98 	int ret = setup_default(state);
99 	if (ret)
100 		return ret;
101 	/* Modify the version byte. */
102 	data.raw[0] = ~data.raw[0];
103 	return 0;
104 }
105 
teardown_unmap(void ** state)106 static int teardown_unmap(void **state)
107 {
108 	/* We need to reset the cached_state in src/lib/ux_locales.c. */
109 	ux_locales_unmap();
110 	return 0;
111 }
112 
113 /* Test items. */
test_ux_locales_get_text(void ** state)114 static void test_ux_locales_get_text(void **state)
115 {
116 	struct ux_locales_test_state *s = *state;
117 	const char *ret;
118 
119 	will_return(_cbfs_alloc, true);
120 	will_return(vb2api_get_locale_id, s->lang_id);
121 	ret = ux_locales_get_text(s->name);
122 	if (s->expect) {
123 		assert_non_null(ret);
124 		assert_string_equal(ret, s->expect);
125 	} else {
126 		assert_null(ret);
127 	}
128 }
129 
test_ux_locales_bad_cbfs(void ** state)130 static void test_ux_locales_bad_cbfs(void **state)
131 {
132 	will_return(_cbfs_alloc, false);
133 	will_return_maybe(vb2api_get_locale_id, 0);
134 	assert_null(ux_locales_get_text("name_1"));
135 }
136 
test_ux_locales_bad_version(void ** state)137 static void test_ux_locales_bad_version(void **state)
138 {
139 	will_return(_cbfs_alloc, true);
140 	will_return(vb2api_get_locale_id, 0);
141 	assert_null(ux_locales_get_text("name_1"));
142 }
143 
test_ux_locales_two_calls(void ** state)144 static void test_ux_locales_two_calls(void **state)
145 {
146 	const char *ret;
147 
148 	/* We do not need to ensure that we cached the cbfs region. */
149 	will_return_always(_cbfs_alloc, true);
150 
151 	/* Call #1: read (15, 60). */
152 	will_return(vb2api_get_locale_id, 60);
153 	ret = ux_locales_get_text("name_15");
154 	assert_non_null(ret);
155 	assert_string_equal(ret, "translation_15_60");
156 
157 	/* Call #2: read (1, 0). */
158 	will_return(vb2api_get_locale_id, 0);
159 	ret = ux_locales_get_text("name_1");
160 	assert_non_null(ret);
161 	assert_string_equal(ret, "translation_1_0");
162 }
163 
test_ux_locales_null_terminated(void ** state)164 static void test_ux_locales_null_terminated(void **state)
165 {
166 	will_return_always(_cbfs_alloc, true);
167 	will_return_always(vb2api_get_locale_id, 8);
168 
169 	/* Verify the access to the very last text. */
170 	assert_non_null(ux_locales_get_text("name_20"));
171 
172 	/* Modify the last 2 bytes from "\x00\x01" to "XX" and unmap, */
173 	data.raw[data.size - 1] = 'X';
174 	data.raw[data.size - 2] = 'X';
175 	ux_locales_unmap();
176 
177 	/* The last few characters are now changed so that the data is not NULL terminated.
178 	   This will prevent us from accessing the last text. */
179 	assert_null(ux_locales_get_text("name_20"));
180 }
181 
182 /*
183  * This macro helps test ux_locales_get_text with `_name` and `_lang_id`.
184  * If `_expect` is NULL, then the function should not find anything.
185  * Otherwise, the function should find the corresponding expect value.
186  */
187 #define UX_LOCALES_GET_TEXT_TEST(_name, _lang_id, _expect)                                     \
188 	((struct CMUnitTest) {                                                                 \
189 		.name = "test_ux_locales_get_text(name=" _name ", lang_id=" #_lang_id          \
190 			", expect=" #_expect ")",                                              \
191 		.test_func = test_ux_locales_get_text,                                         \
192 		.setup_func = setup_default,                                                   \
193 		.teardown_func = teardown_unmap,                                               \
194 		.initial_state = &(struct ux_locales_test_state) {                             \
195 			.name = _name,                                                         \
196 			.lang_id = _lang_id,                                                   \
197 			.expect = _expect,                                                     \
198 		},                                                                             \
199 	})
200 
main(void)201 int main(void)
202 {
203 	const struct CMUnitTest tests[] = {
204 		/* Get text successfully. */
205 		UX_LOCALES_GET_TEXT_TEST("name_1", 0, "translation_1_0"),
206 		/* Get text with name and id both in the middle. */
207 		UX_LOCALES_GET_TEXT_TEST("name_15", 25, "translation_15_25"),
208 		/* Ensure we check the whole string of 'name'.
209 		   ('name_2' is the prefix of 'name_20') */
210 		UX_LOCALES_GET_TEXT_TEST("name_2", 3, NULL),
211 		/* Ensure we check the whole string of 'lang_id'.
212 		   (id:'2' is the prefix of id:'25' in 'name_15') */
213 		UX_LOCALES_GET_TEXT_TEST("name_15", 2, NULL),
214 		/* Ensure we will fallback to 0. */
215 		UX_LOCALES_GET_TEXT_TEST("name_1", 7, "translation_1_0"),
216 		/* Do not search for locale id with unmatched name. */
217 		UX_LOCALES_GET_TEXT_TEST("name_15", 8, NULL),
218 		/* Validity check of lang_id > 100. We will fallback to 0. */
219 		UX_LOCALES_GET_TEXT_TEST("name_1", 100, "translation_1_0"),
220 		/* cbfs not found. */
221 		cmocka_unit_test_setup_teardown(test_ux_locales_bad_cbfs, setup_default,
222 						teardown_unmap),
223 		/* Bad version. */
224 		cmocka_unit_test_setup_teardown(test_ux_locales_bad_version, setup_bad_version,
225 						teardown_unmap),
226 		/* Read multiple times. */
227 		cmocka_unit_test_setup_teardown(test_ux_locales_two_calls, setup_default,
228 						teardown_unmap),
229 		/* Validity check of NULL terminated. */
230 		cmocka_unit_test_setup_teardown(test_ux_locales_null_terminated,
231 						setup_default, teardown_unmap),
232 	};
233 
234 	return cb_run_group_tests(tests, NULL, NULL);
235 }
236