brintos

brintos / linux-shallow public Read only

0
0
Text · 17.2 KiB · 7511442 Raw
537 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * KUnit test for struct string_stream.4 *5 * Copyright (C) 2019, Google LLC.6 * Author: Brendan Higgins <brendanhiggins@google.com>7 */8 9#include <kunit/static_stub.h>10#include <kunit/test.h>11#include <linux/ktime.h>12#include <linux/slab.h>13#include <linux/timekeeping.h>14 15#include "string-stream.h"16 17struct string_stream_test_priv {18	/* For testing resource-managed free. */19	struct string_stream *expected_free_stream;20	bool stream_was_freed;21	bool stream_free_again;22};23 24/* Avoids a cast warning if kfree() is passed direct to kunit_add_action(). */25KUNIT_DEFINE_ACTION_WRAPPER(kfree_wrapper, kfree, const void *);26 27/* Avoids a cast warning if string_stream_destroy() is passed direct to kunit_add_action(). */28KUNIT_DEFINE_ACTION_WRAPPER(cleanup_raw_stream, string_stream_destroy, struct string_stream *);29 30static char *get_concatenated_string(struct kunit *test, struct string_stream *stream)31{32	char *str = string_stream_get_string(stream);33 34	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, str);35	kunit_add_action(test, kfree_wrapper, (void *)str);36 37	return str;38}39 40/* Managed string_stream object is initialized correctly. */41static void string_stream_managed_init_test(struct kunit *test)42{43	struct string_stream *stream;44 45	/* Resource-managed initialization. */46	stream = kunit_alloc_string_stream(test, GFP_KERNEL);47	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream);48 49	KUNIT_EXPECT_EQ(test, stream->length, 0);50	KUNIT_EXPECT_TRUE(test, list_empty(&stream->fragments));51	KUNIT_EXPECT_TRUE(test, (stream->gfp == GFP_KERNEL));52	KUNIT_EXPECT_FALSE(test, stream->append_newlines);53	KUNIT_EXPECT_TRUE(test, string_stream_is_empty(stream));54}55 56/* Unmanaged string_stream object is initialized correctly. */57static void string_stream_unmanaged_init_test(struct kunit *test)58{59	struct string_stream *stream;60 61	stream = alloc_string_stream(GFP_KERNEL);62	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream);63	kunit_add_action(test, cleanup_raw_stream, stream);64 65	KUNIT_EXPECT_EQ(test, stream->length, 0);66	KUNIT_EXPECT_TRUE(test, list_empty(&stream->fragments));67	KUNIT_EXPECT_TRUE(test, (stream->gfp == GFP_KERNEL));68	KUNIT_EXPECT_FALSE(test, stream->append_newlines);69 70	KUNIT_EXPECT_TRUE(test, string_stream_is_empty(stream));71}72 73static void string_stream_destroy_stub(struct string_stream *stream)74{75	struct kunit *fake_test = kunit_get_current_test();76	struct string_stream_test_priv *priv = fake_test->priv;77 78	/* The kunit could own string_streams other than the one we are testing. */79	if (stream == priv->expected_free_stream) {80		if (priv->stream_was_freed)81			priv->stream_free_again = true;82		else83			priv->stream_was_freed = true;84	}85 86	/*87	 * Calling string_stream_destroy() will only call this function again88	 * because the redirection stub is still active.89	 * Avoid calling deactivate_static_stub() or changing current->kunit_test90	 * during cleanup.91	 */92	string_stream_clear(stream);93	kfree(stream);94}95 96/* kunit_free_string_stream() calls string_stream_desrtoy() */97static void string_stream_managed_free_test(struct kunit *test)98{99	struct string_stream_test_priv *priv = test->priv;100 101	priv->expected_free_stream = NULL;102	priv->stream_was_freed = false;103	priv->stream_free_again = false;104 105	kunit_activate_static_stub(test,106				   string_stream_destroy,107				   string_stream_destroy_stub);108 109	priv->expected_free_stream = kunit_alloc_string_stream(test, GFP_KERNEL);110	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv->expected_free_stream);111 112	/* This should call the stub function. */113	kunit_free_string_stream(test, priv->expected_free_stream);114 115	KUNIT_EXPECT_TRUE(test, priv->stream_was_freed);116	KUNIT_EXPECT_FALSE(test, priv->stream_free_again);117}118 119/* string_stream object is freed when test is cleaned up. */120static void string_stream_resource_free_test(struct kunit *test)121{122	struct string_stream_test_priv *priv = test->priv;123	struct kunit *fake_test;124 125	fake_test = kunit_kzalloc(test, sizeof(*fake_test), GFP_KERNEL);126	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, fake_test);127 128	kunit_init_test(fake_test, "string_stream_fake_test", NULL);129	fake_test->priv = priv;130 131	/*132	 * Activate stub before creating string_stream so the133	 * string_stream will be cleaned up first.134	 */135	priv->expected_free_stream = NULL;136	priv->stream_was_freed = false;137	priv->stream_free_again = false;138 139	kunit_activate_static_stub(fake_test,140				   string_stream_destroy,141				   string_stream_destroy_stub);142 143	priv->expected_free_stream = kunit_alloc_string_stream(fake_test, GFP_KERNEL);144	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv->expected_free_stream);145 146	/* Set current->kunit_test to fake_test so the static stub will be called. */147	current->kunit_test = fake_test;148 149	/* Cleanup test - the stub function should be called */150	kunit_cleanup(fake_test);151 152	/* Set current->kunit_test back to current test. */153	current->kunit_test = test;154 155	KUNIT_EXPECT_TRUE(test, priv->stream_was_freed);156	KUNIT_EXPECT_FALSE(test, priv->stream_free_again);157}158 159/*160 * Add a series of lines to a string_stream. Check that all lines161 * appear in the correct order and no characters are dropped.162 */163static void string_stream_line_add_test(struct kunit *test)164{165	struct string_stream *stream;166	char line[60];167	char *concat_string, *pos, *string_end;168	size_t len, total_len;169	int num_lines, i;170 171	stream = kunit_alloc_string_stream(test, GFP_KERNEL);172	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream);173 174	/* Add series of sequence numbered lines */175	total_len = 0;176	for (i = 0; i < 100; ++i) {177		len = snprintf(line, sizeof(line),178			"The quick brown fox jumps over the lazy penguin %d\n", i);179 180		/* Sanity-check that our test string isn't truncated */181		KUNIT_ASSERT_LT(test, len, sizeof(line));182 183		string_stream_add(stream, line);184		total_len += len;185	}186	num_lines = i;187 188	concat_string = get_concatenated_string(test, stream);189	KUNIT_EXPECT_NOT_ERR_OR_NULL(test, concat_string);190	KUNIT_EXPECT_EQ(test, strlen(concat_string), total_len);191 192	/*193	 * Split the concatenated string at the newlines and check that194	 * all the original added strings are present.195	 */196	pos = concat_string;197	for (i = 0; i < num_lines; ++i) {198		string_end = strchr(pos, '\n');199		KUNIT_EXPECT_NOT_NULL(test, string_end);200 201		/* Convert to NULL-terminated string */202		*string_end = '\0';203 204		snprintf(line, sizeof(line),205			 "The quick brown fox jumps over the lazy penguin %d", i);206		KUNIT_EXPECT_STREQ(test, pos, line);207 208		pos = string_end + 1;209	}210 211	/* There shouldn't be any more data after this */212	KUNIT_EXPECT_EQ(test, strlen(pos), 0);213}214 215/* Add a series of lines of variable length to a string_stream. */216static void string_stream_variable_length_line_test(struct kunit *test)217{218	static const char line[] =219		"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"220		" 0123456789!$%^&*()_-+={}[]:;@'~#<>,.?/|";221	struct string_stream *stream;222	struct rnd_state rnd;223	char *concat_string, *pos, *string_end;224	size_t offset, total_len;225	int num_lines, i;226 227	stream = kunit_alloc_string_stream(test, GFP_KERNEL);228	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream);229 230	/*231	 * Log many lines of varying lengths until we have created232	 * many fragments.233	 * The "randomness" must be repeatable.234	 */235	prandom_seed_state(&rnd, 3141592653589793238ULL);236	total_len = 0;237	for (i = 0; i < 100; ++i) {238		offset = prandom_u32_state(&rnd) % (sizeof(line) - 1);239		string_stream_add(stream, "%s\n", &line[offset]);240		total_len += sizeof(line) - offset;241	}242	num_lines = i;243 244	concat_string = get_concatenated_string(test, stream);245	KUNIT_EXPECT_NOT_ERR_OR_NULL(test, concat_string);246	KUNIT_EXPECT_EQ(test, strlen(concat_string), total_len);247 248	/*249	 * Split the concatenated string at the newlines and check that250	 * all the original added strings are present.251	 */252	prandom_seed_state(&rnd, 3141592653589793238ULL);253	pos = concat_string;254	for (i = 0; i < num_lines; ++i) {255		string_end = strchr(pos, '\n');256		KUNIT_EXPECT_NOT_NULL(test, string_end);257 258		/* Convert to NULL-terminated string */259		*string_end = '\0';260 261		offset = prandom_u32_state(&rnd) % (sizeof(line) - 1);262		KUNIT_EXPECT_STREQ(test, pos, &line[offset]);263 264		pos = string_end + 1;265	}266 267	/* There shouldn't be any more data after this */268	KUNIT_EXPECT_EQ(test, strlen(pos), 0);269}270 271/* Appending the content of one string stream to another. */272static void string_stream_append_test(struct kunit *test)273{274	static const char * const strings_1[] = {275		"one", "two", "three", "four", "five", "six",276		"seven", "eight", "nine", "ten",277	};278	static const char * const strings_2[] = {279		"Apple", "Pear", "Orange", "Banana", "Grape", "Apricot",280	};281	struct string_stream *stream_1, *stream_2;282	const char *stream1_content_before_append, *stream_2_content;283	char *combined_content;284	size_t combined_length;285	int i;286 287	stream_1 = kunit_alloc_string_stream(test, GFP_KERNEL);288	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream_1);289 290	stream_2 = kunit_alloc_string_stream(test, GFP_KERNEL);291	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream_2);292 293	/* Append content of empty stream to empty stream */294	string_stream_append(stream_1, stream_2);295	KUNIT_EXPECT_EQ(test, strlen(get_concatenated_string(test, stream_1)), 0);296 297	/* Add some data to stream_1 */298	for (i = 0; i < ARRAY_SIZE(strings_1); ++i)299		string_stream_add(stream_1, "%s\n", strings_1[i]);300 301	stream1_content_before_append = get_concatenated_string(test, stream_1);302 303	/* Append content of empty stream to non-empty stream */304	string_stream_append(stream_1, stream_2);305	KUNIT_EXPECT_STREQ(test, get_concatenated_string(test, stream_1),306			   stream1_content_before_append);307 308	/* Add some data to stream_2 */309	for (i = 0; i < ARRAY_SIZE(strings_2); ++i)310		string_stream_add(stream_2, "%s\n", strings_2[i]);311 312	/* Append content of non-empty stream to non-empty stream */313	string_stream_append(stream_1, stream_2);314 315	/*316	 * End result should be the original content of stream_1 plus317	 * the content of stream_2.318	 */319	stream_2_content = get_concatenated_string(test, stream_2);320	combined_length = strlen(stream1_content_before_append) + strlen(stream_2_content);321	combined_length++; /* for terminating \0 */322	combined_content = kunit_kmalloc(test, combined_length, GFP_KERNEL);323	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, combined_content);324	snprintf(combined_content, combined_length, "%s%s",325		 stream1_content_before_append, stream_2_content);326 327	KUNIT_EXPECT_STREQ(test, get_concatenated_string(test, stream_1), combined_content);328 329	/* Append content of non-empty stream to empty stream */330	kunit_free_string_stream(test, stream_1);331 332	stream_1 = kunit_alloc_string_stream(test, GFP_KERNEL);333	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream_1);334 335	string_stream_append(stream_1, stream_2);336	KUNIT_EXPECT_STREQ(test, get_concatenated_string(test, stream_1), stream_2_content);337}338 339/* Appending the content of one string stream to one with auto-newlining. */340static void string_stream_append_auto_newline_test(struct kunit *test)341{342	struct string_stream *stream_1, *stream_2;343 344	/* Stream 1 has newline appending enabled */345	stream_1 = kunit_alloc_string_stream(test, GFP_KERNEL);346	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream_1);347	string_stream_set_append_newlines(stream_1, true);348	KUNIT_EXPECT_TRUE(test, stream_1->append_newlines);349 350	/* Stream 2 does not append newlines */351	stream_2 = kunit_alloc_string_stream(test, GFP_KERNEL);352	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream_2);353 354	/* Appending a stream with a newline should not add another newline */355	string_stream_add(stream_1, "Original string\n");356	string_stream_add(stream_2, "Appended content\n");357	string_stream_add(stream_2, "More stuff\n");358	string_stream_append(stream_1, stream_2);359	KUNIT_EXPECT_STREQ(test, get_concatenated_string(test, stream_1),360			   "Original string\nAppended content\nMore stuff\n");361 362	kunit_free_string_stream(test, stream_2);363	stream_2 = kunit_alloc_string_stream(test, GFP_KERNEL);364	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream_2);365 366	/*367	 * Appending a stream without newline should add a final newline.368	 * The appended string_stream is treated as a single string so newlines369	 * should not be inserted between fragments.370	 */371	string_stream_add(stream_2, "Another");372	string_stream_add(stream_2, "And again");373	string_stream_append(stream_1, stream_2);374	KUNIT_EXPECT_STREQ(test, get_concatenated_string(test, stream_1),375			   "Original string\nAppended content\nMore stuff\nAnotherAnd again\n");376}377 378/* Adding an empty string should not create a fragment. */379static void string_stream_append_empty_string_test(struct kunit *test)380{381	struct string_stream *stream;382	int original_frag_count;383 384	stream = kunit_alloc_string_stream(test, GFP_KERNEL);385	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream);386 387	/* Formatted empty string */388	string_stream_add(stream, "%s", "");389	KUNIT_EXPECT_TRUE(test, string_stream_is_empty(stream));390	KUNIT_EXPECT_TRUE(test, list_empty(&stream->fragments));391 392	/* Adding an empty string to a non-empty stream */393	string_stream_add(stream, "Add this line");394	original_frag_count = list_count_nodes(&stream->fragments);395 396	string_stream_add(stream, "%s", "");397	KUNIT_EXPECT_EQ(test, list_count_nodes(&stream->fragments), original_frag_count);398	KUNIT_EXPECT_STREQ(test, get_concatenated_string(test, stream), "Add this line");399}400 401/* Adding strings without automatic newline appending */402static void string_stream_no_auto_newline_test(struct kunit *test)403{404	struct string_stream *stream;405 406	stream = kunit_alloc_string_stream(test, GFP_KERNEL);407	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream);408 409	/*410	 * Add some strings with and without newlines. All formatted newlines411	 * should be preserved. It should not add any extra newlines.412	 */413	string_stream_add(stream, "One");414	string_stream_add(stream, "Two\n");415	string_stream_add(stream, "%s\n", "Three");416	string_stream_add(stream, "%s", "Four\n");417	string_stream_add(stream, "Five\n%s", "Six");418	string_stream_add(stream, "Seven\n\n");419	string_stream_add(stream, "Eight");420	KUNIT_EXPECT_STREQ(test, get_concatenated_string(test, stream),421			   "OneTwo\nThree\nFour\nFive\nSixSeven\n\nEight");422}423 424/* Adding strings with automatic newline appending */425static void string_stream_auto_newline_test(struct kunit *test)426{427	struct string_stream *stream;428 429	stream = kunit_alloc_string_stream(test, GFP_KERNEL);430	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream);431 432	string_stream_set_append_newlines(stream, true);433	KUNIT_EXPECT_TRUE(test, stream->append_newlines);434 435	/*436	 * Add some strings with and without newlines. Newlines should437	 * be appended to lines that do not end with \n, but newlines438	 * resulting from the formatting should not be changed.439	 */440	string_stream_add(stream, "One");441	string_stream_add(stream, "Two\n");442	string_stream_add(stream, "%s\n", "Three");443	string_stream_add(stream, "%s", "Four\n");444	string_stream_add(stream, "Five\n%s", "Six");445	string_stream_add(stream, "Seven\n\n");446	string_stream_add(stream, "Eight");447	KUNIT_EXPECT_STREQ(test, get_concatenated_string(test, stream),448			   "One\nTwo\nThree\nFour\nFive\nSix\nSeven\n\nEight\n");449}450 451/*452 * This doesn't actually "test" anything. It reports time taken453 * and memory used for logging a large number of lines.454 */455static void string_stream_performance_test(struct kunit *test)456{457	struct string_stream_fragment *frag_container;458	struct string_stream *stream;459	char test_line[101];460	ktime_t start_time, end_time;461	size_t len, bytes_requested, actual_bytes_used, total_string_length;462	int offset, i;463 464	stream = kunit_alloc_string_stream(test, GFP_KERNEL);465	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream);466 467	memset(test_line, 'x', sizeof(test_line) - 1);468	test_line[sizeof(test_line) - 1] = '\0';469 470	start_time = ktime_get();471	for (i = 0; i < 10000; i++) {472		offset = i % (sizeof(test_line) - 1);473		string_stream_add(stream, "%s: %d\n", &test_line[offset], i);474	}475	end_time = ktime_get();476 477	/*478	 * Calculate memory used. This doesn't include invisible479	 * overhead due to kernel allocator fragment size rounding.480	 */481	bytes_requested = sizeof(*stream);482	actual_bytes_used = ksize(stream);483	total_string_length = 0;484 485	list_for_each_entry(frag_container, &stream->fragments, node) {486		bytes_requested += sizeof(*frag_container);487		actual_bytes_used += ksize(frag_container);488 489		len = strlen(frag_container->fragment);490		total_string_length += len;491		bytes_requested += len + 1; /* +1 for '\0' */492		actual_bytes_used += ksize(frag_container->fragment);493	}494 495	kunit_info(test, "Time elapsed:           %lld us\n",496		   ktime_us_delta(end_time, start_time));497	kunit_info(test, "Total string length:    %zu\n", total_string_length);498	kunit_info(test, "Bytes requested:        %zu\n", bytes_requested);499	kunit_info(test, "Actual bytes allocated: %zu\n", actual_bytes_used);500}501 502static int string_stream_test_init(struct kunit *test)503{504	struct string_stream_test_priv *priv;505 506	priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL);507	if (!priv)508		return -ENOMEM;509 510	test->priv = priv;511 512	return 0;513}514 515static struct kunit_case string_stream_test_cases[] = {516	KUNIT_CASE(string_stream_managed_init_test),517	KUNIT_CASE(string_stream_unmanaged_init_test),518	KUNIT_CASE(string_stream_managed_free_test),519	KUNIT_CASE(string_stream_resource_free_test),520	KUNIT_CASE(string_stream_line_add_test),521	KUNIT_CASE(string_stream_variable_length_line_test),522	KUNIT_CASE(string_stream_append_test),523	KUNIT_CASE(string_stream_append_auto_newline_test),524	KUNIT_CASE(string_stream_append_empty_string_test),525	KUNIT_CASE(string_stream_no_auto_newline_test),526	KUNIT_CASE(string_stream_auto_newline_test),527	KUNIT_CASE(string_stream_performance_test),528	{}529};530 531static struct kunit_suite string_stream_test_suite = {532	.name = "string-stream-test",533	.test_cases = string_stream_test_cases,534	.init = string_stream_test_init,535};536kunit_test_suites(&string_stream_test_suite);537