xref: /linux/lib/tests/cmdline_kunit.c (revision 995832b2cebe6969d1b42635db698803ee31294d)
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Test cases for API provided by cmdline.c
4  */
5 
6 #include <kunit/test.h>
7 #include <linux/kernel.h>
8 #include <linux/random.h>
9 #include <linux/sizes.h>
10 #include <linux/string.h>
11 
12 static const char *cmdline_test_strings[] = {
13 	"\"\"", ""  , "=" , "\"-", ","    , "-,"   , ",-"   , "-" ,
14 	"+,"  , "--", ",,", "''" , "\"\",", "\",\"", "-\"\"", "\"",
15 };
16 
17 static const int cmdline_test_values[] = {
18 	1, 1, 1, 1, 2, 3, 2, 3,
19 	1, 3, 2, 1, 1, 1, 3, 1,
20 };
21 
22 static_assert(ARRAY_SIZE(cmdline_test_strings) == ARRAY_SIZE(cmdline_test_values));
23 
24 static const char *cmdline_test_range_strings[] = {
25 	"-7" , "--7"  , "-1-2"    , "7--9",
26 	"7-" , "-7--9", "7-9,"    , "9-7" ,
27 	"5-a", "a-5"  , "5-8"     , ",8-5",
28 	"+,1", "-,4"  , "-3,0-1,6", "4,-" ,
29 	" +2", " -9"  , "0-1,-3,6", "- 9" ,
30 };
31 
32 static const int cmdline_test_range_values[][16] = {
33 	{ 1, -7, }, { 0, -0, }, { 4, -1, 0, +1, 2, }, { 0, 7, },
34 	{ 0, +7, }, { 0, -7, }, { 3, +7, 8, +9, 0, }, { 0, 9, },
35 	{ 0, +5, }, { 0, -0, }, { 4, +5, 6, +7, 8, }, { 0, 0, },
36 	{ 0, +0, }, { 0, -0, }, { 4, -3, 0, +1, 6, }, { 1, 4, },
37 	{ 0, +0, }, { 0, -0, }, { 4, +0, 1, -3, 6, }, { 0, 0, },
38 };
39 
40 static_assert(ARRAY_SIZE(cmdline_test_range_strings) == ARRAY_SIZE(cmdline_test_range_values));
41 
42 static void cmdline_do_one_test(struct kunit *test, const char *in, int rc, int offset)
43 {
44 	const char *fmt = "Pattern: %s";
45 	const char *out = in;
46 	int dummy;
47 	int ret;
48 
49 	ret = get_option((char **)&out, &dummy);
50 
51 	KUNIT_EXPECT_EQ_MSG(test, ret, rc, fmt, in);
52 	KUNIT_EXPECT_PTR_EQ_MSG(test, out, in + offset, fmt, in);
53 }
54 
55 static void cmdline_test_noint(struct kunit *test)
56 {
57 	unsigned int i = 0;
58 
59 	do {
60 		const char *str = cmdline_test_strings[i];
61 		int rc = 0;
62 		int offset;
63 
64 		/* Only first and leading '-' will advance the pointer */
65 		offset = !!(*str == '-');
66 		cmdline_do_one_test(test, str, rc, offset);
67 	} while (++i < ARRAY_SIZE(cmdline_test_strings));
68 }
69 
70 static void cmdline_test_lead_int(struct kunit *test)
71 {
72 	unsigned int i = 0;
73 	char in[32];
74 
75 	do {
76 		const char *str = cmdline_test_strings[i];
77 		int rc = cmdline_test_values[i];
78 		int offset;
79 
80 		sprintf(in, "%u%s", get_random_u8(), str);
81 		/* Only first '-' after the number will advance the pointer */
82 		offset = strlen(in) - strlen(str) + !!(rc == 2);
83 		cmdline_do_one_test(test, in, rc, offset);
84 	} while (++i < ARRAY_SIZE(cmdline_test_strings));
85 }
86 
87 static void cmdline_test_tail_int(struct kunit *test)
88 {
89 	unsigned int i = 0;
90 	char in[32];
91 
92 	do {
93 		const char *str = cmdline_test_strings[i];
94 		/* When "" or "-" the result will be valid integer */
95 		int rc = strcmp(str, "") ? (strcmp(str, "-") ? 0 : 1) : 1;
96 		int offset;
97 
98 		sprintf(in, "%s%u", str, get_random_u8());
99 		/*
100 		 * Only first and leading '-' not followed by integer
101 		 * will advance the pointer.
102 		 */
103 		offset = rc ? strlen(in) : !!(*str == '-');
104 		cmdline_do_one_test(test, in, rc, offset);
105 	} while (++i < ARRAY_SIZE(cmdline_test_strings));
106 }
107 
108 static void cmdline_do_one_range_test(struct kunit *test, const char *in,
109 				      unsigned int n, const int *e)
110 {
111 	unsigned int i;
112 	int r[16];
113 	int *p;
114 
115 	memset(r, 0, sizeof(r));
116 	get_options(in, ARRAY_SIZE(r), r);
117 	KUNIT_EXPECT_EQ_MSG(test, r[0], e[0], "in test %u (parsed) expected %d numbers, got %d",
118 			    n, e[0], r[0]);
119 	for (i = 1; i < ARRAY_SIZE(r); i++)
120 		KUNIT_EXPECT_EQ_MSG(test, r[i], e[i], "in test %u at %u", n, i);
121 
122 	memset(r, 0, sizeof(r));
123 	get_options(in, 0, r);
124 	KUNIT_EXPECT_EQ_MSG(test, r[0], e[0], "in test %u (validated) expected %d numbers, got %d",
125 			    n, e[0], r[0]);
126 
127 	p = memchr_inv(&r[1], 0, sizeof(r) - sizeof(r[0]));
128 	KUNIT_EXPECT_PTR_EQ_MSG(test, p, NULL, "in test %u at %td out of bound", n, p - r);
129 }
130 
131 static void cmdline_test_range(struct kunit *test)
132 {
133 	unsigned int i = 0;
134 
135 	do {
136 		const char *str = cmdline_test_range_strings[i];
137 		const int *e = cmdline_test_range_values[i];
138 
139 		cmdline_do_one_range_test(test, str, i, e);
140 	} while (++i < ARRAY_SIZE(cmdline_test_range_strings));
141 }
142 
143 static void cmdline_test_next_arg_quoted_value(struct kunit *test)
144 {
145 	char in[] = "foo=\"bar baz\" qux=1";
146 	char *next, *param, *val;
147 
148 	next = next_arg(in, &param, &val);
149 	KUNIT_EXPECT_STREQ(test, param, "foo");
150 	KUNIT_ASSERT_NOT_NULL(test, val);
151 	KUNIT_EXPECT_STREQ(test, val, "bar baz");
152 	KUNIT_EXPECT_STREQ(test, next, "qux=1");
153 
154 	next = next_arg(next, &param, &val);
155 	KUNIT_EXPECT_STREQ(test, param, "qux");
156 	KUNIT_ASSERT_NOT_NULL(test, val);
157 	KUNIT_EXPECT_STREQ(test, val, "1");
158 	KUNIT_EXPECT_STREQ(test, next, "");
159 }
160 
161 static void cmdline_test_next_arg_bare_quote_regression(struct kunit *test)
162 {
163 	char in[] = "foo=bar \"";
164 	char *next, *param, *val;
165 
166 	next = next_arg(in, &param, &val);
167 	KUNIT_EXPECT_STREQ(test, param, "foo");
168 	KUNIT_ASSERT_NOT_NULL(test, val);
169 	KUNIT_EXPECT_STREQ(test, val, "bar");
170 	KUNIT_EXPECT_STREQ(test, next, "\"");
171 
172 	/* This hits the i == 0 quoted-token case fixed by 9847f21225c4. */
173 	next = next_arg(next, &param, &val);
174 	KUNIT_EXPECT_STREQ(test, param, "");
175 	KUNIT_EXPECT_PTR_EQ(test, val, NULL);
176 	KUNIT_EXPECT_STREQ(test, next, "");
177 }
178 
179 static void cmdline_test_next_arg_mixed_tokens(struct kunit *test)
180 {
181 	char in[] = "bbb= jjj kkk=\"a=b\"";
182 	char *next, *param, *val;
183 
184 	next = next_arg(in, &param, &val);
185 	KUNIT_EXPECT_STREQ(test, param, "bbb");
186 	KUNIT_ASSERT_NOT_NULL(test, val);
187 	KUNIT_EXPECT_STREQ(test, val, "");
188 	KUNIT_EXPECT_STREQ(test, next, "jjj kkk=\"a=b\"");
189 
190 	next = next_arg(next, &param, &val);
191 	KUNIT_EXPECT_STREQ(test, param, "jjj");
192 	KUNIT_EXPECT_NULL(test, val);
193 	KUNIT_EXPECT_STREQ(test, next, "kkk=\"a=b\"");
194 
195 	next = next_arg(next, &param, &val);
196 	KUNIT_EXPECT_STREQ(test, param, "kkk");
197 	KUNIT_ASSERT_NOT_NULL(test, val);
198 	KUNIT_EXPECT_STREQ(test, val, "a=b");
199 	KUNIT_EXPECT_STREQ(test, next, "");
200 }
201 
202 struct cmdline_test_memparse_entry {
203 	const char *input;
204 	const char *unrecognized;
205 	unsigned long long result;
206 };
207 
208 static const struct cmdline_test_memparse_entry testdata[] = {
209 	{ "0",				"",	0ULL },
210 	{ "1",				"",	1ULL },
211 	{ "a",				"a",	0ULL },
212 	{ "k",				"k",	0ULL },
213 	{ "E",				"E",	0ULL },
214 	{ "0xb",			"",	11ULL },
215 	{ "0xz",			"x",	0ULL },
216 	{ "1234",			"",	1234ULL },
217 	{ "04567",			"",	2423ULL },
218 	{ "0x9876",			"",	39030LL },
219 	{ "05678",			"8",	375ULL },
220 	{ "0xabcdefz",			"z",	11259375ULL },
221 	{ "0cdba",			"c",	0ULL },
222 	{ "4K",				"",	SZ_4K },
223 	{ "0x10k@0xaaaabbbb",		"@",	SZ_16K },
224 	{ "32M",			"",	SZ_32M },
225 	{ "067m:foo",			":",	55 * SZ_1M },
226 	{ "2G;bar=baz",			";",	SZ_2G },
227 	{ "07gz",			"z",	7ULL * SZ_1G },
228 	{ "3T+data",			"+",	3 * SZ_1T },
229 	{ "04t,ro",			",",	SZ_4T },
230 	{ "012p",			"",	11258999068426240ULL },
231 	{ "7P,sync",			",",	7881299347898368ULL },
232 	{ "0x2e",			"",	46ULL },
233 	{ "2E and more",		" ",	2305843009213693952ULL },
234 	{ "18446744073709551615",	"",	ULLONG_MAX },
235 	{ "0xffffffffffffffff0",	"",	ULLONG_MAX },
236 	{ "1111111111111111111T",	"",	ULLONG_MAX },
237 	{ "222222222222222222222G",	"",	ULLONG_MAX },
238 	{ "3333333333333333333333M",	"",	ULLONG_MAX },
239 };
240 
241 static void cmdline_test_memparse(struct kunit *test)
242 {
243 	const struct cmdline_test_memparse_entry *e;
244 	unsigned long long ret;
245 	char *retptr;
246 
247 	for (e = testdata; e < testdata + ARRAY_SIZE(testdata); e++) {
248 		ret = memparse(e->input, &retptr);
249 		KUNIT_EXPECT_EQ_MSG(test, ret, e->result,
250 				    "    when parsing '%s'", e->input);
251 		KUNIT_EXPECT_EQ_MSG(test, *retptr, *e->unrecognized,
252 				    "    when parsing '%s'", e->input);
253 	}
254 }
255 
256 static struct kunit_case cmdline_test_cases[] = {
257 	KUNIT_CASE(cmdline_test_noint),
258 	KUNIT_CASE(cmdline_test_lead_int),
259 	KUNIT_CASE(cmdline_test_tail_int),
260 	KUNIT_CASE(cmdline_test_range),
261 	KUNIT_CASE(cmdline_test_next_arg_quoted_value),
262 	KUNIT_CASE(cmdline_test_next_arg_bare_quote_regression),
263 	KUNIT_CASE(cmdline_test_next_arg_mixed_tokens),
264 	KUNIT_CASE(cmdline_test_memparse),
265 	{}
266 };
267 
268 static struct kunit_suite cmdline_test_suite = {
269 	.name = "cmdline",
270 	.test_cases = cmdline_test_cases,
271 };
272 kunit_test_suite(cmdline_test_suite);
273 
274 MODULE_DESCRIPTION("Test cases for API provided by cmdline.c");
275 MODULE_LICENSE("GPL");
276