brintos

brintos / linux-shallow public Read only

0
0
Text · 15.9 KiB · c853046 Raw
630 lines · c
1// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause2/*3 * Test cases for lib/string_helpers.c module.4 */5#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt6 7#include <kunit/test.h>8#include <linux/array_size.h>9#include <linux/kernel.h>10#include <linux/random.h>11#include <linux/string.h>12#include <linux/string_helpers.h>13 14static void test_string_check_buf(struct kunit *test,15				  const char *name, unsigned int flags,16				  char *in, size_t p,17				  char *out_real, size_t q_real,18				  char *out_test, size_t q_test)19{20	KUNIT_ASSERT_EQ_MSG(test, q_real, q_test, "name:%s", name);21	KUNIT_EXPECT_MEMEQ_MSG(test, out_test, out_real, q_test,22			       "name:%s", name);23}24 25struct test_string {26	const char *in;27	const char *out;28	unsigned int flags;29};30 31static const struct test_string strings[] = {32	{33		.in = "\\f\\ \\n\\r\\t\\v",34		.out = "\f\\ \n\r\t\v",35		.flags = UNESCAPE_SPACE,36	},37	{38		.in = "\\40\\1\\387\\0064\\05\\040\\8a\\110\\777",39		.out = " \001\00387\0064\005 \\8aH?7",40		.flags = UNESCAPE_OCTAL,41	},42	{43		.in = "\\xv\\xa\\x2c\\xD\\x6f2",44		.out = "\\xv\n,\ro2",45		.flags = UNESCAPE_HEX,46	},47	{48		.in = "\\h\\\\\\\"\\a\\e\\",49		.out = "\\h\\\"\a\e\\",50		.flags = UNESCAPE_SPECIAL,51	},52};53 54static void test_string_unescape(struct kunit *test,55				 const char *name, unsigned int flags,56				 bool inplace)57{58	int q_real = 256;59	char *in = kunit_kzalloc(test, q_real, GFP_KERNEL);60	char *out_test = kunit_kzalloc(test, q_real, GFP_KERNEL);61	char *out_real = kunit_kzalloc(test, q_real, GFP_KERNEL);62	int i, p = 0, q_test = 0;63 64	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, in);65	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, out_test);66	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, out_real);67 68	for (i = 0; i < ARRAY_SIZE(strings); i++) {69		const char *s = strings[i].in;70		int len = strlen(strings[i].in);71 72		/* Copy string to in buffer */73		memcpy(&in[p], s, len);74		p += len;75 76		/* Copy expected result for given flags */77		if (flags & strings[i].flags) {78			s = strings[i].out;79			len = strlen(strings[i].out);80		}81		memcpy(&out_test[q_test], s, len);82		q_test += len;83	}84	in[p++] = '\0';85 86	/* Call string_unescape and compare result */87	if (inplace) {88		memcpy(out_real, in, p);89		if (flags == UNESCAPE_ANY)90			q_real = string_unescape_any_inplace(out_real);91		else92			q_real = string_unescape_inplace(out_real, flags);93	} else if (flags == UNESCAPE_ANY) {94		q_real = string_unescape_any(in, out_real, q_real);95	} else {96		q_real = string_unescape(in, out_real, q_real, flags);97	}98 99	test_string_check_buf(test, name, flags, in, p - 1, out_real, q_real,100			      out_test, q_test);101}102 103struct test_string_1 {104	const char *out;105	unsigned int flags;106};107 108#define	TEST_STRING_2_MAX_S1		32109struct test_string_2 {110	const char *in;111	struct test_string_1 s1[TEST_STRING_2_MAX_S1];112};113 114#define	TEST_STRING_2_DICT_0		NULL115static const struct test_string_2 escape0[] = {{116	.in = "\f\\ \n\r\t\v",117	.s1 = {{118		.out = "\\f\\ \\n\\r\\t\\v",119		.flags = ESCAPE_SPACE,120	},{121		.out = "\\f\\134\\040\\n\\r\\t\\v",122		.flags = ESCAPE_SPACE | ESCAPE_OCTAL,123	},{124		.out = "\\f\\x5c\\x20\\n\\r\\t\\v",125		.flags = ESCAPE_SPACE | ESCAPE_HEX,126	},{127		/* terminator */128	}}129},{130	.in = "\\h\\\"\a\e\\",131	.s1 = {{132		.out = "\\\\h\\\\\\\"\\a\\e\\\\",133		.flags = ESCAPE_SPECIAL,134	},{135		.out = "\\\\\\150\\\\\\\"\\a\\e\\\\",136		.flags = ESCAPE_SPECIAL | ESCAPE_OCTAL,137	},{138		.out = "\\\\\\x68\\\\\\\"\\a\\e\\\\",139		.flags = ESCAPE_SPECIAL | ESCAPE_HEX,140	},{141		/* terminator */142	}}143},{144	.in = "\eb \\C\007\"\x90\r]",145	.s1 = {{146		.out = "\eb \\C\007\"\x90\\r]",147		.flags = ESCAPE_SPACE,148	},{149		.out = "\\eb \\\\C\\a\\\"\x90\r]",150		.flags = ESCAPE_SPECIAL,151	},{152		.out = "\\eb \\\\C\\a\\\"\x90\\r]",153		.flags = ESCAPE_SPACE | ESCAPE_SPECIAL,154	},{155		.out = "\\033\\142\\040\\134\\103\\007\\042\\220\\015\\135",156		.flags = ESCAPE_OCTAL,157	},{158		.out = "\\033\\142\\040\\134\\103\\007\\042\\220\\r\\135",159		.flags = ESCAPE_SPACE | ESCAPE_OCTAL,160	},{161		.out = "\\e\\142\\040\\\\\\103\\a\\\"\\220\\015\\135",162		.flags = ESCAPE_SPECIAL | ESCAPE_OCTAL,163	},{164		.out = "\\e\\142\\040\\\\\\103\\a\\\"\\220\\r\\135",165		.flags = ESCAPE_SPACE | ESCAPE_SPECIAL | ESCAPE_OCTAL,166	},{167		.out = "\eb \\C\007\"\x90\r]",168		.flags = ESCAPE_NP,169	},{170		.out = "\eb \\C\007\"\x90\\r]",171		.flags = ESCAPE_SPACE | ESCAPE_NP,172	},{173		.out = "\\eb \\C\\a\"\x90\r]",174		.flags = ESCAPE_SPECIAL | ESCAPE_NP,175	},{176		.out = "\\eb \\C\\a\"\x90\\r]",177		.flags = ESCAPE_SPACE | ESCAPE_SPECIAL | ESCAPE_NP,178	},{179		.out = "\\033b \\C\\007\"\\220\\015]",180		.flags = ESCAPE_OCTAL | ESCAPE_NP,181	},{182		.out = "\\033b \\C\\007\"\\220\\r]",183		.flags = ESCAPE_SPACE | ESCAPE_OCTAL | ESCAPE_NP,184	},{185		.out = "\\eb \\C\\a\"\\220\\r]",186		.flags = ESCAPE_SPECIAL | ESCAPE_SPACE | ESCAPE_OCTAL |187			 ESCAPE_NP,188	},{189		.out = "\\x1bb \\C\\x07\"\\x90\\x0d]",190		.flags = ESCAPE_NP | ESCAPE_HEX,191	},{192		/* terminator */193	}}194},{195	.in = "\007 \eb\"\x90\xCF\r",196	.s1 = {{197		.out = "\007 \eb\"\\220\\317\r",198		.flags = ESCAPE_OCTAL | ESCAPE_NA,199	},{200		.out = "\007 \eb\"\\x90\\xcf\r",201		.flags = ESCAPE_HEX | ESCAPE_NA,202	},{203		.out = "\007 \eb\"\x90\xCF\r",204		.flags = ESCAPE_NA,205	},{206		/* terminator */207	}}208},{209	/* terminator */210}};211 212#define	TEST_STRING_2_DICT_1		"b\\ \t\r\xCF"213static const struct test_string_2 escape1[] = {{214	.in = "\f\\ \n\r\t\v",215	.s1 = {{216		.out = "\f\\134\\040\n\\015\\011\v",217		.flags = ESCAPE_OCTAL,218	},{219		.out = "\f\\x5c\\x20\n\\x0d\\x09\v",220		.flags = ESCAPE_HEX,221	},{222		.out = "\f\\134\\040\n\\015\\011\v",223		.flags = ESCAPE_ANY | ESCAPE_APPEND,224	},{225		.out = "\\014\\134\\040\\012\\015\\011\\013",226		.flags = ESCAPE_OCTAL | ESCAPE_APPEND | ESCAPE_NAP,227	},{228		.out = "\\x0c\\x5c\\x20\\x0a\\x0d\\x09\\x0b",229		.flags = ESCAPE_HEX | ESCAPE_APPEND | ESCAPE_NAP,230	},{231		.out = "\f\\134\\040\n\\015\\011\v",232		.flags = ESCAPE_OCTAL | ESCAPE_APPEND | ESCAPE_NA,233	},{234		.out = "\f\\x5c\\x20\n\\x0d\\x09\v",235		.flags = ESCAPE_HEX | ESCAPE_APPEND | ESCAPE_NA,236	},{237		/* terminator */238	}}239},{240	.in = "\\h\\\"\a\xCF\e\\",241	.s1 = {{242		.out = "\\134h\\134\"\a\\317\e\\134",243		.flags = ESCAPE_OCTAL,244	},{245		.out = "\\134h\\134\"\a\\317\e\\134",246		.flags = ESCAPE_ANY | ESCAPE_APPEND,247	},{248		.out = "\\134h\\134\"\\007\\317\\033\\134",249		.flags = ESCAPE_OCTAL | ESCAPE_APPEND | ESCAPE_NAP,250	},{251		.out = "\\134h\\134\"\a\\317\e\\134",252		.flags = ESCAPE_OCTAL | ESCAPE_APPEND | ESCAPE_NA,253	},{254		/* terminator */255	}}256},{257	.in = "\eb \\C\007\"\x90\r]",258	.s1 = {{259		.out = "\e\\142\\040\\134C\007\"\x90\\015]",260		.flags = ESCAPE_OCTAL,261	},{262		/* terminator */263	}}264},{265	.in = "\007 \eb\"\x90\xCF\r",266	.s1 = {{267		.out = "\007 \eb\"\x90\xCF\r",268		.flags = ESCAPE_NA,269	},{270		.out = "\007 \eb\"\x90\xCF\r",271		.flags = ESCAPE_SPACE | ESCAPE_NA,272	},{273		.out = "\007 \eb\"\x90\xCF\r",274		.flags = ESCAPE_SPECIAL | ESCAPE_NA,275	},{276		.out = "\007 \eb\"\x90\xCF\r",277		.flags = ESCAPE_SPACE | ESCAPE_SPECIAL | ESCAPE_NA,278	},{279		.out = "\007 \eb\"\x90\\317\r",280		.flags = ESCAPE_OCTAL | ESCAPE_NA,281	},{282		.out = "\007 \eb\"\x90\\317\r",283		.flags = ESCAPE_SPACE | ESCAPE_OCTAL | ESCAPE_NA,284	},{285		.out = "\007 \eb\"\x90\\317\r",286		.flags = ESCAPE_SPECIAL | ESCAPE_OCTAL | ESCAPE_NA,287	},{288		.out = "\007 \eb\"\x90\\317\r",289		.flags = ESCAPE_ANY | ESCAPE_NA,290	},{291		.out = "\007 \eb\"\x90\\xcf\r",292		.flags = ESCAPE_HEX | ESCAPE_NA,293	},{294		.out = "\007 \eb\"\x90\\xcf\r",295		.flags = ESCAPE_SPACE | ESCAPE_HEX | ESCAPE_NA,296	},{297		.out = "\007 \eb\"\x90\\xcf\r",298		.flags = ESCAPE_SPECIAL | ESCAPE_HEX | ESCAPE_NA,299	},{300		.out = "\007 \eb\"\x90\\xcf\r",301		.flags = ESCAPE_SPACE | ESCAPE_SPECIAL | ESCAPE_HEX | ESCAPE_NA,302	},{303		/* terminator */304	}}305},{306	.in = "\007 \eb\"\x90\xCF\r",307	.s1 = {{308		.out = "\007 \eb\"\x90\xCF\r",309		.flags = ESCAPE_NAP,310	},{311		.out = "\007 \eb\"\x90\xCF\\r",312		.flags = ESCAPE_SPACE | ESCAPE_NAP,313	},{314		.out = "\007 \eb\"\x90\xCF\r",315		.flags = ESCAPE_SPECIAL | ESCAPE_NAP,316	},{317		.out = "\007 \eb\"\x90\xCF\\r",318		.flags = ESCAPE_SPACE | ESCAPE_SPECIAL | ESCAPE_NAP,319	},{320		.out = "\007 \eb\"\x90\\317\\015",321		.flags = ESCAPE_OCTAL | ESCAPE_NAP,322	},{323		.out = "\007 \eb\"\x90\\317\\r",324		.flags = ESCAPE_SPACE | ESCAPE_OCTAL | ESCAPE_NAP,325	},{326		.out = "\007 \eb\"\x90\\317\\015",327		.flags = ESCAPE_SPECIAL | ESCAPE_OCTAL | ESCAPE_NAP,328	},{329		.out = "\007 \eb\"\x90\\317\r",330		.flags = ESCAPE_ANY | ESCAPE_NAP,331	},{332		.out = "\007 \eb\"\x90\\xcf\\x0d",333		.flags = ESCAPE_HEX | ESCAPE_NAP,334	},{335		.out = "\007 \eb\"\x90\\xcf\\r",336		.flags = ESCAPE_SPACE | ESCAPE_HEX | ESCAPE_NAP,337	},{338		.out = "\007 \eb\"\x90\\xcf\\x0d",339		.flags = ESCAPE_SPECIAL | ESCAPE_HEX | ESCAPE_NAP,340	},{341		.out = "\007 \eb\"\x90\\xcf\\r",342		.flags = ESCAPE_SPACE | ESCAPE_SPECIAL | ESCAPE_HEX | ESCAPE_NAP,343	},{344		/* terminator */345	}}346},{347	/* terminator */348}};349 350static const struct test_string strings_upper[] = {351	{352		.in = "abcdefgh1234567890test",353		.out = "ABCDEFGH1234567890TEST",354	},355	{356		.in = "abCdeFgH1234567890TesT",357		.out = "ABCDEFGH1234567890TEST",358	},359};360 361static const struct test_string strings_lower[] = {362	{363		.in = "ABCDEFGH1234567890TEST",364		.out = "abcdefgh1234567890test",365	},366	{367		.in = "abCdeFgH1234567890TesT",368		.out = "abcdefgh1234567890test",369	},370};371 372static const char *test_string_find_match(const struct test_string_2 *s2,373					  unsigned int flags)374{375	const struct test_string_1 *s1 = s2->s1;376	unsigned int i;377 378	if (!flags)379		return s2->in;380 381	/* Test cases are NULL-aware */382	flags &= ~ESCAPE_NULL;383 384	/* ESCAPE_OCTAL has a higher priority */385	if (flags & ESCAPE_OCTAL)386		flags &= ~ESCAPE_HEX;387 388	for (i = 0; i < TEST_STRING_2_MAX_S1 && s1->out; i++, s1++)389		if (s1->flags == flags)390			return s1->out;391	return NULL;392}393 394static void395test_string_escape_overflow(struct kunit *test,396			    const char *in, int p, unsigned int flags, const char *esc,397			    int q_test, const char *name)398{399	int q_real;400 401	q_real = string_escape_mem(in, p, NULL, 0, flags, esc);402	KUNIT_EXPECT_EQ_MSG(test, q_real, q_test, "name:%s: flags:%#x", name, flags);403}404 405static void test_string_escape(struct kunit *test, const char *name,406			       const struct test_string_2 *s2,407			       unsigned int flags, const char *esc)408{409	size_t out_size = 512;410	char *out_test = kunit_kzalloc(test, out_size, GFP_KERNEL);411	char *out_real = kunit_kzalloc(test, out_size, GFP_KERNEL);412	char *in = kunit_kzalloc(test, 256, GFP_KERNEL);413	int p = 0, q_test = 0;414	int q_real;415 416	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, out_test);417	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, out_real);418	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, in);419 420	for (; s2->in; s2++) {421		const char *out;422		int len;423 424		/* NULL injection */425		if (flags & ESCAPE_NULL) {426			in[p++] = '\0';427			/* '\0' passes isascii() test */428			if (flags & ESCAPE_NA && !(flags & ESCAPE_APPEND && esc)) {429				out_test[q_test++] = '\0';430			} else {431				out_test[q_test++] = '\\';432				out_test[q_test++] = '0';433			}434		}435 436		/* Don't try strings that have no output */437		out = test_string_find_match(s2, flags);438		if (!out)439			continue;440 441		/* Copy string to in buffer */442		len = strlen(s2->in);443		memcpy(&in[p], s2->in, len);444		p += len;445 446		/* Copy expected result for given flags */447		len = strlen(out);448		memcpy(&out_test[q_test], out, len);449		q_test += len;450	}451 452	q_real = string_escape_mem(in, p, out_real, out_size, flags, esc);453 454	test_string_check_buf(test, name, flags, in, p, out_real, q_real, out_test,455			      q_test);456 457	test_string_escape_overflow(test, in, p, flags, esc, q_test, name);458}459 460#define string_get_size_maxbuf 16461#define test_string_get_size_one(size, blk_size, exp_result10, exp_result2)      \462	do {                                                                     \463		BUILD_BUG_ON(sizeof(exp_result10) >= string_get_size_maxbuf);    \464		BUILD_BUG_ON(sizeof(exp_result2) >= string_get_size_maxbuf);     \465		__test_string_get_size(test, (size), (blk_size), (exp_result10), \466				       (exp_result2));                           \467	} while (0)468 469 470static void test_string_get_size_check(struct kunit *test,471				       const char *units,472				       const char *exp,473				       char *res,474				       const u64 size,475				       const u64 blk_size)476{477	KUNIT_EXPECT_MEMEQ_MSG(test, res, exp, strlen(exp) + 1,478		"string_get_size(size = %llu, blk_size = %llu, units = %s)",479		size, blk_size, units);480}481 482static void __strchrcut(char *dst, const char *src, const char *cut)483{484	const char *from = src;485	size_t len;486 487	do {488		len = strcspn(from, cut);489		memcpy(dst, from, len);490		dst += len;491		from += len;492	} while (*from++);493	*dst = '\0';494}495 496static void __test_string_get_size_one(struct kunit *test,497				       const u64 size, const u64 blk_size,498				       const char *exp_result10,499				       const char *exp_result2,500				       enum string_size_units units,501				       const char *cut)502{503	char buf10[string_get_size_maxbuf];504	char buf2[string_get_size_maxbuf];505	char exp10[string_get_size_maxbuf];506	char exp2[string_get_size_maxbuf];507	char prefix10[64];508	char prefix2[64];509 510	sprintf(prefix10, "STRING_UNITS_10 [%s]", cut);511	sprintf(prefix2, "STRING_UNITS_2 [%s]", cut);512 513	__strchrcut(exp10, exp_result10, cut);514	__strchrcut(exp2, exp_result2, cut);515 516	string_get_size(size, blk_size, STRING_UNITS_10 | units, buf10, sizeof(buf10));517	string_get_size(size, blk_size, STRING_UNITS_2 | units, buf2, sizeof(buf2));518 519	test_string_get_size_check(test, prefix10, exp10, buf10, size, blk_size);520	test_string_get_size_check(test, prefix2, exp2, buf2, size, blk_size);521}522 523static void __test_string_get_size(struct kunit *test,524				   const u64 size, const u64 blk_size,525				   const char *exp_result10,526				   const char *exp_result2)527{528	struct {529		enum string_size_units units;530		const char *cut;531	} get_size_test_cases[] = {532		{ 0, "" },533		{ STRING_UNITS_NO_SPACE, " " },534		{ STRING_UNITS_NO_SPACE | STRING_UNITS_NO_BYTES, " B" },535		{ STRING_UNITS_NO_BYTES, "B" },536	};537	int i;538 539	for (i = 0; i < ARRAY_SIZE(get_size_test_cases); i++)540		__test_string_get_size_one(test, size, blk_size,541					   exp_result10, exp_result2,542					   get_size_test_cases[i].units,543					   get_size_test_cases[i].cut);544}545 546static void test_get_size(struct kunit *test)547{548	/* small values */549	test_string_get_size_one(0, 512, "0 B", "0 B");550	test_string_get_size_one(1, 512, "512 B", "512 B");551	test_string_get_size_one(1100, 1, "1.10 kB", "1.07 KiB");552 553	/* normal values */554	test_string_get_size_one(16384, 512, "8.39 MB", "8.00 MiB");555	test_string_get_size_one(500118192, 512, "256 GB", "238 GiB");556	test_string_get_size_one(8192, 4096, "33.6 MB", "32.0 MiB");557 558	/* weird block sizes */559	test_string_get_size_one(3000, 1900, "5.70 MB", "5.44 MiB");560 561	/* huge values */562	test_string_get_size_one(U64_MAX, 4096, "75.6 ZB", "64.0 ZiB");563	test_string_get_size_one(4096, U64_MAX, "75.6 ZB", "64.0 ZiB");564}565 566static void test_upper_lower(struct kunit *test)567{568	char *dst;569	int i;570 571	for (i = 0; i < ARRAY_SIZE(strings_upper); i++) {572		const char *s = strings_upper[i].in;573		int len = strlen(strings_upper[i].in) + 1;574 575		dst = kmalloc(len, GFP_KERNEL);576		KUNIT_ASSERT_NOT_NULL(test, dst);577 578		string_upper(dst, s);579		KUNIT_EXPECT_STREQ(test, dst, strings_upper[i].out);580		kfree(dst);581	}582 583	for (i = 0; i < ARRAY_SIZE(strings_lower); i++) {584		const char *s = strings_lower[i].in;585		int len = strlen(strings_lower[i].in) + 1;586 587		dst = kmalloc(len, GFP_KERNEL);588		KUNIT_ASSERT_NOT_NULL(test, dst);589 590		string_lower(dst, s);591		KUNIT_EXPECT_STREQ(test, dst, strings_lower[i].out);592		kfree(dst);593	}594}595 596static void test_unescape(struct kunit *test)597{598	unsigned int i;599 600	for (i = 0; i < UNESCAPE_ALL_MASK + 1; i++)601		test_string_unescape(test, "unescape", i, false);602	test_string_unescape(test, "unescape inplace",603			     get_random_u32_below(UNESCAPE_ALL_MASK + 1), true);604 605	/* Without dictionary */606	for (i = 0; i < ESCAPE_ALL_MASK + 1; i++)607		test_string_escape(test, "escape 0", escape0, i, TEST_STRING_2_DICT_0);608 609	/* With dictionary */610	for (i = 0; i < ESCAPE_ALL_MASK + 1; i++)611		test_string_escape(test, "escape 1", escape1, i, TEST_STRING_2_DICT_1);612}613 614static struct kunit_case string_helpers_test_cases[] = {615	KUNIT_CASE(test_get_size),616	KUNIT_CASE(test_upper_lower),617	KUNIT_CASE(test_unescape),618	{}619};620 621static struct kunit_suite string_helpers_test_suite = {622	.name = "string_helpers",623	.test_cases = string_helpers_test_cases,624};625 626kunit_test_suites(&string_helpers_test_suite);627 628MODULE_DESCRIPTION("Test cases for string helpers module");629MODULE_LICENSE("Dual BSD/GPL");630