xref: /linux/drivers/platform/wmi/tests/string_kunit.c (revision 23b0f90ba871f096474e1c27c3d14f455189d2d9)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * KUnit test for the ACPI-WMI string conversion code.
4  *
5  * Copyright (C) 2025 Armin Wolf <W_Armin@gmx.de>
6  */
7 
8 #include <linux/module.h>
9 #include <linux/slab.h>
10 #include <linux/string.h>
11 #include <linux/wmi.h>
12 
13 #include <kunit/resource.h>
14 #include <kunit/test.h>
15 
16 #include <asm/byteorder.h>
17 
18 struct wmi_string_param {
19 	const char *name;
20 	const struct wmi_string *wmi_string;
21 	/*
22 	 * Remember that using sizeof() on a struct wmi_string will
23 	 * always return a size of two bytes due to the flexible
24 	 * array member!
25 	 */
26 	size_t wmi_string_length;
27 	const u8 *utf8_string;
28 	size_t utf8_string_length;
29 };
30 
31 #define TEST_WMI_STRING_LENGTH 12
32 
33 static const struct wmi_string test_wmi_string = {
34 	.length = cpu_to_le16(10),
35 	.chars = {
36 		cpu_to_le16(u'T'),
37 		cpu_to_le16(u'E'),
38 		cpu_to_le16(u'S'),
39 		cpu_to_le16(u'T'),
40 		cpu_to_le16(u'\0'),
41 	},
42 };
43 
44 static const u8 test_utf8_string[] = "TEST";
45 
46 #define SPECIAL_WMI_STRING_LENGTH 14
47 
48 static const struct wmi_string special_wmi_string = {
49 	.length = cpu_to_le16(12),
50 	.chars = {
51 		cpu_to_le16(u'Ä'),
52 		cpu_to_le16(u'Ö'),
53 		cpu_to_le16(u'Ü'),
54 		cpu_to_le16(u'ß'),
55 		cpu_to_le16(u'€'),
56 		cpu_to_le16(u'\0'),
57 	},
58 };
59 
60 static const u8 special_utf8_string[] = "ÄÖÜ߀";
61 
62 #define MULTI_POINT_WMI_STRING_LENGTH 12
63 
64 static const struct wmi_string multi_point_wmi_string = {
65 	.length = cpu_to_le16(10),
66 	.chars = {
67 		cpu_to_le16(u'K'),
68 		/* �� */
69 		cpu_to_le16(0xD83D),
70 		cpu_to_le16(0xDC27),
71 		cpu_to_le16(u'!'),
72 		cpu_to_le16(u'\0'),
73 	},
74 };
75 
76 static const u8 multi_point_utf8_string[] = "K��!";
77 
78 #define PADDED_TEST_WMI_STRING_LENGTH 14
79 
80 static const struct wmi_string padded_test_wmi_string = {
81 	.length = cpu_to_le16(12),
82 	.chars = {
83 		cpu_to_le16(u'T'),
84 		cpu_to_le16(u'E'),
85 		cpu_to_le16(u'S'),
86 		cpu_to_le16(u'T'),
87 		cpu_to_le16(u'\0'),
88 		cpu_to_le16(u'\0'),
89 	},
90 };
91 
92 static const u8 padded_test_utf8_string[] = "TEST\0";
93 
94 #define OVERSIZED_TEST_WMI_STRING_LENGTH 14
95 
96 static const struct wmi_string oversized_test_wmi_string = {
97 	.length = cpu_to_le16(8),
98 	.chars = {
99 		cpu_to_le16(u'T'),
100 		cpu_to_le16(u'E'),
101 		cpu_to_le16(u'S'),
102 		cpu_to_le16(u'T'),
103 		cpu_to_le16(u'!'),
104 		cpu_to_le16(u'\0'),
105 	},
106 };
107 
108 static const u8 oversized_test_utf8_string[] = "TEST!";
109 
110 #define INVALID_TEST_WMI_STRING_LENGTH 14
111 
112 static const struct wmi_string invalid_test_wmi_string = {
113 	.length = cpu_to_le16(12),
114 	.chars = {
115 		cpu_to_le16(u'T'),
116 		/* ��, with low surrogate missing */
117 		cpu_to_le16(0xD83D),
118 		cpu_to_le16(u'E'),
119 		cpu_to_le16(u'S'),
120 		cpu_to_le16(u'T'),
121 		cpu_to_le16(u'\0'),
122 	},
123 };
124 
125 /* We have to split the string here to end the hex escape sequence */
126 static const u8 invalid_test_utf8_string[] = "T" "\xF0\x9F" "EST";
127 
128 static const struct wmi_string_param wmi_string_params_array[] = {
129 	{
130 		.name = "ascii_string",
131 		.wmi_string = &test_wmi_string,
132 		.wmi_string_length = TEST_WMI_STRING_LENGTH,
133 		.utf8_string = test_utf8_string,
134 		.utf8_string_length = sizeof(test_utf8_string),
135 	},
136 	{
137 		.name = "special_string",
138 		.wmi_string = &special_wmi_string,
139 		.wmi_string_length = SPECIAL_WMI_STRING_LENGTH,
140 		.utf8_string = special_utf8_string,
141 		.utf8_string_length = sizeof(special_utf8_string),
142 	},
143 	{
144 		.name = "multi_point_string",
145 		.wmi_string = &multi_point_wmi_string,
146 		.wmi_string_length = MULTI_POINT_WMI_STRING_LENGTH,
147 		.utf8_string = multi_point_utf8_string,
148 		.utf8_string_length = sizeof(multi_point_utf8_string),
149 	},
150 };
151 
152 static void wmi_string_param_get_desc(const struct wmi_string_param *param, char *desc)
153 {
154 	strscpy(desc, param->name, KUNIT_PARAM_DESC_SIZE);
155 }
156 
157 KUNIT_ARRAY_PARAM(wmi_string, wmi_string_params_array, wmi_string_param_get_desc);
158 
159 static void wmi_string_to_utf8s_test(struct kunit *test)
160 {
161 	const struct wmi_string_param *param = test->param_value;
162 	ssize_t ret;
163 	u8 *result;
164 
165 	result = kunit_kzalloc(test, param->utf8_string_length, GFP_KERNEL);
166 	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, result);
167 
168 	ret = wmi_string_to_utf8s(param->wmi_string, result, param->utf8_string_length);
169 
170 	KUNIT_EXPECT_EQ(test, ret, param->utf8_string_length - 1);
171 	KUNIT_EXPECT_MEMEQ(test, result, param->utf8_string, param->utf8_string_length);
172 }
173 
174 static void wmi_string_from_utf8s_test(struct kunit *test)
175 {
176 	const struct wmi_string_param *param = test->param_value;
177 	struct wmi_string *result;
178 	size_t max_chars;
179 	ssize_t ret;
180 
181 	max_chars = (param->wmi_string_length - sizeof(*result)) / 2;
182 	result = kunit_kzalloc(test, param->wmi_string_length, GFP_KERNEL);
183 	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, result);
184 
185 	ret = wmi_string_from_utf8s(result, max_chars, param->utf8_string,
186 				    param->utf8_string_length);
187 
188 	KUNIT_EXPECT_EQ(test, ret, max_chars - 1);
189 	KUNIT_EXPECT_MEMEQ(test, result, param->wmi_string, param->wmi_string_length);
190 }
191 
192 static void wmi_string_to_utf8s_padded_test(struct kunit *test)
193 {
194 	u8 result[sizeof(padded_test_utf8_string)];
195 	ssize_t ret;
196 
197 	ret = wmi_string_to_utf8s(&padded_test_wmi_string, result, sizeof(result));
198 
199 	KUNIT_EXPECT_EQ(test, ret, sizeof(test_utf8_string) - 1);
200 	KUNIT_EXPECT_MEMEQ(test, result, test_utf8_string, sizeof(test_utf8_string));
201 }
202 
203 static void wmi_string_from_utf8s_padded_test(struct kunit *test)
204 {
205 	struct wmi_string *result;
206 	size_t max_chars;
207 	ssize_t ret;
208 
209 	max_chars = (PADDED_TEST_WMI_STRING_LENGTH - sizeof(*result)) / 2;
210 	result = kunit_kzalloc(test, PADDED_TEST_WMI_STRING_LENGTH, GFP_KERNEL);
211 	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, result);
212 
213 	ret = wmi_string_from_utf8s(result, max_chars, padded_test_utf8_string,
214 				    sizeof(padded_test_utf8_string));
215 
216 	KUNIT_EXPECT_EQ(test, ret, sizeof(test_utf8_string) - 1);
217 	KUNIT_EXPECT_MEMEQ(test, result, &test_wmi_string, sizeof(test_wmi_string));
218 }
219 
220 static void wmi_string_to_utf8s_oversized_test(struct kunit *test)
221 {
222 	u8 result[sizeof(oversized_test_utf8_string)];
223 	ssize_t ret;
224 
225 	ret = wmi_string_to_utf8s(&oversized_test_wmi_string, result, sizeof(result));
226 
227 	KUNIT_EXPECT_EQ(test, ret, sizeof(test_utf8_string) - 1);
228 	KUNIT_EXPECT_MEMEQ(test, result, test_utf8_string, sizeof(test_utf8_string));
229 }
230 
231 static void wmi_string_from_utf8s_oversized_test(struct kunit *test)
232 {
233 	struct wmi_string *result;
234 	size_t max_chars;
235 	ssize_t ret;
236 
237 	max_chars = (TEST_WMI_STRING_LENGTH - sizeof(*result)) / 2;
238 	result = kunit_kzalloc(test, TEST_WMI_STRING_LENGTH, GFP_KERNEL);
239 	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, result);
240 
241 	ret = wmi_string_from_utf8s(result, max_chars, oversized_test_utf8_string,
242 				    sizeof(oversized_test_utf8_string));
243 
244 	KUNIT_EXPECT_EQ(test, ret, sizeof(test_utf8_string) - 1);
245 	KUNIT_EXPECT_MEMEQ(test, result, &test_wmi_string, sizeof(test_wmi_string));
246 }
247 
248 static void wmi_string_to_utf8s_invalid_test(struct kunit *test)
249 {
250 	u8 result[sizeof(invalid_test_utf8_string)];
251 	ssize_t ret;
252 
253 	ret = wmi_string_to_utf8s(&invalid_test_wmi_string, result, sizeof(result));
254 
255 	KUNIT_EXPECT_EQ(test, ret, sizeof(test_utf8_string) - 1);
256 	KUNIT_EXPECT_MEMEQ(test, result, test_utf8_string, sizeof(test_utf8_string));
257 }
258 
259 static void wmi_string_from_utf8s_invalid_test(struct kunit *test)
260 {
261 	struct wmi_string *result;
262 	size_t max_chars;
263 	ssize_t ret;
264 
265 	max_chars = (INVALID_TEST_WMI_STRING_LENGTH - sizeof(*result)) / 2;
266 	result = kunit_kzalloc(test, INVALID_TEST_WMI_STRING_LENGTH, GFP_KERNEL);
267 	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, result);
268 
269 	ret = wmi_string_from_utf8s(result, max_chars, invalid_test_utf8_string,
270 				    sizeof(invalid_test_utf8_string));
271 
272 	KUNIT_EXPECT_EQ(test, ret, -EINVAL);
273 }
274 
275 static struct kunit_case wmi_string_test_cases[] = {
276 	KUNIT_CASE_PARAM(wmi_string_to_utf8s_test, wmi_string_gen_params),
277 	KUNIT_CASE_PARAM(wmi_string_from_utf8s_test, wmi_string_gen_params),
278 	KUNIT_CASE(wmi_string_to_utf8s_padded_test),
279 	KUNIT_CASE(wmi_string_from_utf8s_padded_test),
280 	KUNIT_CASE(wmi_string_to_utf8s_oversized_test),
281 	KUNIT_CASE(wmi_string_from_utf8s_oversized_test),
282 	KUNIT_CASE(wmi_string_to_utf8s_invalid_test),
283 	KUNIT_CASE(wmi_string_from_utf8s_invalid_test),
284 	{}
285 };
286 
287 static struct kunit_suite wmi_string_test_suite = {
288 	.name = "wmi_string",
289 	.test_cases = wmi_string_test_cases,
290 };
291 
292 kunit_test_suite(wmi_string_test_suite);
293 
294 MODULE_AUTHOR("Armin Wolf <W_Armin@gmx.de>");
295 MODULE_DESCRIPTION("KUnit test for the ACPI-WMI string conversion code");
296 MODULE_LICENSE("GPL");
297