blob: d5eeef707c93e4c8a6e11de2af119621dc83a272 [file] [log] [blame]
Hsuan Ting Chenabaa4b52023-07-05 19:07:23 +08001/* 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 )
27const 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)
39struct {
40 unsigned char raw[MAX_DATA_SIZE];
41 size_t size;
42} data;
43
44/* Mock functions. */
45void 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. */
51void *_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
63uint32_t vb2api_get_locale_id(struct vb2_context *ctx)
64{
65 return mock_type(uint32_t);
66}
67
68struct 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. */
75struct ux_locales_test_state {
76 const char *name;
77 uint32_t lang_id;
78 const char *expect;
79};
80
81/* Setup and teardown functions. */
82static 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
96static 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
106static 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. */
114static 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
130static 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
137static 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
144static 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
164static 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(_test_name, _name, _lang_id, _expect) \
188 ((struct CMUnitTest) { \
189 .name = _test_name, \
190 .test_func = test_ux_locales_get_text, \
191 .setup_func = setup_default, \
192 .teardown_func = teardown_unmap, \
193 .initial_state = &(struct ux_locales_test_state) \
194 { \
195 .name = _name, \
196 .lang_id = _lang_id, \
197 .expect = _expect, \
198 }, \
199 })
200
201/*
202 * When exporting test results to xml files, cmocka doesn't escape double quotes for test names.
203 * Therefore, double quotes in CMUnitTest.name will lead to invalid xml files, causing build
204 * failure (with JUNIT_OUTPUT=y). As a result, we can only use _expect for CMUnitTest.name in
205 * the macro, but not #_expect.
206 */
207#define UX_LOCALES_GET_TEXT_FOUND_TEST(_name, _lang_id, _expect) \
208 (_UX_LOCALES_GET_TEXT_TEST \
209 ( \
210 ( \
211 "test_ux_locales_get_text_found(name=" _name \
212 ", lang_id=" #_lang_id ", expect=" _expect ")" \
213 ), \
214 _name, _lang_id, _expect \
215 ))
216
217#define UX_LOCALES_GET_TEXT_NOT_FOUND_TEST(_name, _lang_id) \
218 (_UX_LOCALES_GET_TEXT_TEST \
219 ( \
220 ( \
221 "test_ux_locales_get_text_not_found(name=" _name \
222 ", lang_id=" #_lang_id ")" \
223 ), \
224 _name, _lang_id, NULL \
225 ))
226
227int main(void)
228{
229 const struct CMUnitTest tests[] = {
230 /* Get text successfully. */
231 UX_LOCALES_GET_TEXT_FOUND_TEST("name_1", 0, "translation_1_0"),
232 /* Get text with name and id both in the middle. */
233 UX_LOCALES_GET_TEXT_FOUND_TEST("name_15", 25, "translation_15_25"),
234 /* Ensure we check the whole string of 'name'.
235 ('name_2' is the prefix of 'name_20') */
236 UX_LOCALES_GET_TEXT_NOT_FOUND_TEST("name_2", 3),
237 /* Ensure we check the whole string of 'lang_id'.
238 (id:'2' is the prefix of id:'25' in 'name_15') */
239 UX_LOCALES_GET_TEXT_NOT_FOUND_TEST("name_15", 2),
240 /* Ensure we will fallback to 0. */
241 UX_LOCALES_GET_TEXT_FOUND_TEST("name_1", 7, "translation_1_0"),
242 /* Do not search for locale id with unmatched name. */
243 UX_LOCALES_GET_TEXT_NOT_FOUND_TEST("name_15", 8),
244 /* Validity check of lang_id > 100. We will fallback to 0. */
245 UX_LOCALES_GET_TEXT_FOUND_TEST("name_1", 100, "translation_1_0"),
246 /* cbfs not found. */
247 cmocka_unit_test_setup_teardown(test_ux_locales_bad_cbfs, setup_default,
248 teardown_unmap),
249 /* Bad version. */
250 cmocka_unit_test_setup_teardown(test_ux_locales_bad_version, setup_bad_version,
251 teardown_unmap),
252 /* Read multiple times. */
253 cmocka_unit_test_setup_teardown(test_ux_locales_two_calls, setup_default,
254 teardown_unmap),
255 /* Validity check of NULL terminated. */
256 cmocka_unit_test_setup_teardown(test_ux_locales_null_terminated,
257 setup_default, teardown_unmap),
258 };
259
260 return cb_run_group_tests(tests, NULL, NULL);
261}