xref: /linux-6.15/lib/kunit/string-stream-test.c (revision a3fdf784)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * KUnit test for struct string_stream.
4  *
5  * Copyright (C) 2019, Google LLC.
6  * Author: Brendan Higgins <[email protected]>
7  */
8 
9 #include <kunit/test.h>
10 #include <linux/slab.h>
11 
12 #include "string-stream.h"
13 
14 /* This avoids a cast warning if kfree() is passed direct to kunit_add_action(). */
15 static void kfree_wrapper(void *p)
16 {
17 	kfree(p);
18 }
19 
20 static char *get_concatenated_string(struct kunit *test, struct string_stream *stream)
21 {
22 	char *str = string_stream_get_string(stream);
23 
24 	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, str);
25 	kunit_add_action(test, kfree_wrapper, (void *)str);
26 
27 	return str;
28 }
29 
30 /* string_stream object is initialized correctly. */
31 static void string_stream_init_test(struct kunit *test)
32 {
33 	struct string_stream *stream;
34 
35 	stream = kunit_alloc_string_stream(test, GFP_KERNEL);
36 	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream);
37 
38 	KUNIT_EXPECT_EQ(test, stream->length, 0);
39 	KUNIT_EXPECT_TRUE(test, list_empty(&stream->fragments));
40 	KUNIT_EXPECT_TRUE(test, (stream->gfp == GFP_KERNEL));
41 	KUNIT_EXPECT_FALSE(test, stream->append_newlines);
42 	KUNIT_EXPECT_TRUE(test, string_stream_is_empty(stream));
43 }
44 
45 /*
46  * Add a series of lines to a string_stream. Check that all lines
47  * appear in the correct order and no characters are dropped.
48  */
49 static void string_stream_line_add_test(struct kunit *test)
50 {
51 	struct string_stream *stream;
52 	char line[60];
53 	char *concat_string, *pos, *string_end;
54 	size_t len, total_len;
55 	int num_lines, i;
56 
57 	stream = kunit_alloc_string_stream(test, GFP_KERNEL);
58 	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream);
59 
60 	/* Add series of sequence numbered lines */
61 	total_len = 0;
62 	for (i = 0; i < 100; ++i) {
63 		len = snprintf(line, sizeof(line),
64 			"The quick brown fox jumps over the lazy penguin %d\n", i);
65 
66 		/* Sanity-check that our test string isn't truncated */
67 		KUNIT_ASSERT_LT(test, len, sizeof(line));
68 
69 		string_stream_add(stream, line);
70 		total_len += len;
71 	}
72 	num_lines = i;
73 
74 	concat_string = get_concatenated_string(test, stream);
75 	KUNIT_EXPECT_NOT_ERR_OR_NULL(test, concat_string);
76 	KUNIT_EXPECT_EQ(test, strlen(concat_string), total_len);
77 
78 	/*
79 	 * Split the concatenated string at the newlines and check that
80 	 * all the original added strings are present.
81 	 */
82 	pos = concat_string;
83 	for (i = 0; i < num_lines; ++i) {
84 		string_end = strchr(pos, '\n');
85 		KUNIT_EXPECT_NOT_NULL(test, string_end);
86 
87 		/* Convert to NULL-terminated string */
88 		*string_end = '\0';
89 
90 		snprintf(line, sizeof(line),
91 			 "The quick brown fox jumps over the lazy penguin %d", i);
92 		KUNIT_EXPECT_STREQ(test, pos, line);
93 
94 		pos = string_end + 1;
95 	}
96 
97 	/* There shouldn't be any more data after this */
98 	KUNIT_EXPECT_EQ(test, strlen(pos), 0);
99 }
100 
101 /* Add a series of lines of variable length to a string_stream. */
102 static void string_stream_variable_length_line_test(struct kunit *test)
103 {
104 	static const char line[] =
105 		"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
106 		" 0123456789!$%^&*()_-+={}[]:;@'~#<>,.?/|";
107 	struct string_stream *stream;
108 	struct rnd_state rnd;
109 	char *concat_string, *pos, *string_end;
110 	size_t offset, total_len;
111 	int num_lines, i;
112 
113 	stream = kunit_alloc_string_stream(test, GFP_KERNEL);
114 	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream);
115 
116 	/*
117 	 * Log many lines of varying lengths until we have created
118 	 * many fragments.
119 	 * The "randomness" must be repeatable.
120 	 */
121 	prandom_seed_state(&rnd, 3141592653589793238ULL);
122 	total_len = 0;
123 	for (i = 0; i < 100; ++i) {
124 		offset = prandom_u32_state(&rnd) % (sizeof(line) - 1);
125 		string_stream_add(stream, "%s\n", &line[offset]);
126 		total_len += sizeof(line) - offset;
127 	}
128 	num_lines = i;
129 
130 	concat_string = get_concatenated_string(test, stream);
131 	KUNIT_EXPECT_NOT_ERR_OR_NULL(test, concat_string);
132 	KUNIT_EXPECT_EQ(test, strlen(concat_string), total_len);
133 
134 	/*
135 	 * Split the concatenated string at the newlines and check that
136 	 * all the original added strings are present.
137 	 */
138 	prandom_seed_state(&rnd, 3141592653589793238ULL);
139 	pos = concat_string;
140 	for (i = 0; i < num_lines; ++i) {
141 		string_end = strchr(pos, '\n');
142 		KUNIT_EXPECT_NOT_NULL(test, string_end);
143 
144 		/* Convert to NULL-terminated string */
145 		*string_end = '\0';
146 
147 		offset = prandom_u32_state(&rnd) % (sizeof(line) - 1);
148 		KUNIT_EXPECT_STREQ(test, pos, &line[offset]);
149 
150 		pos = string_end + 1;
151 	}
152 
153 	/* There shouldn't be any more data after this */
154 	KUNIT_EXPECT_EQ(test, strlen(pos), 0);
155 }
156 
157 /* Appending the content of one string stream to another. */
158 static void string_stream_append_test(struct kunit *test)
159 {
160 	static const char * const strings_1[] = {
161 		"one", "two", "three", "four", "five", "six",
162 		"seven", "eight", "nine", "ten",
163 	};
164 	static const char * const strings_2[] = {
165 		"Apple", "Pear", "Orange", "Banana", "Grape", "Apricot",
166 	};
167 	struct string_stream *stream_1, *stream_2;
168 	const char *stream1_content_before_append, *stream_2_content;
169 	char *combined_content;
170 	size_t combined_length;
171 	int i;
172 
173 	stream_1 = kunit_alloc_string_stream(test, GFP_KERNEL);
174 	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream_1);
175 
176 	stream_2 = kunit_alloc_string_stream(test, GFP_KERNEL);
177 	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream_2);
178 
179 	/* Append content of empty stream to empty stream */
180 	string_stream_append(stream_1, stream_2);
181 	KUNIT_EXPECT_EQ(test, strlen(get_concatenated_string(test, stream_1)), 0);
182 
183 	/* Add some data to stream_1 */
184 	for (i = 0; i < ARRAY_SIZE(strings_1); ++i)
185 		string_stream_add(stream_1, "%s\n", strings_1[i]);
186 
187 	stream1_content_before_append = get_concatenated_string(test, stream_1);
188 
189 	/* Append content of empty stream to non-empty stream */
190 	string_stream_append(stream_1, stream_2);
191 	KUNIT_EXPECT_STREQ(test, get_concatenated_string(test, stream_1),
192 			   stream1_content_before_append);
193 
194 	/* Add some data to stream_2 */
195 	for (i = 0; i < ARRAY_SIZE(strings_2); ++i)
196 		string_stream_add(stream_2, "%s\n", strings_2[i]);
197 
198 	/* Append content of non-empty stream to non-empty stream */
199 	string_stream_append(stream_1, stream_2);
200 
201 	/*
202 	 * End result should be the original content of stream_1 plus
203 	 * the content of stream_2.
204 	 */
205 	stream_2_content = get_concatenated_string(test, stream_2);
206 	combined_length = strlen(stream1_content_before_append) + strlen(stream_2_content);
207 	combined_length++; /* for terminating \0 */
208 	combined_content = kunit_kmalloc(test, combined_length, GFP_KERNEL);
209 	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, combined_content);
210 	snprintf(combined_content, combined_length, "%s%s",
211 		 stream1_content_before_append, stream_2_content);
212 
213 	KUNIT_EXPECT_STREQ(test, get_concatenated_string(test, stream_1), combined_content);
214 
215 	/* Append content of non-empty stream to empty stream */
216 	kunit_free_string_stream(test, stream_1);
217 
218 	stream_1 = kunit_alloc_string_stream(test, GFP_KERNEL);
219 	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream_1);
220 
221 	string_stream_append(stream_1, stream_2);
222 	KUNIT_EXPECT_STREQ(test, get_concatenated_string(test, stream_1), stream_2_content);
223 }
224 
225 /* Appending the content of one string stream to one with auto-newlining. */
226 static void string_stream_append_auto_newline_test(struct kunit *test)
227 {
228 	struct string_stream *stream_1, *stream_2;
229 
230 	/* Stream 1 has newline appending enabled */
231 	stream_1 = kunit_alloc_string_stream(test, GFP_KERNEL);
232 	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream_1);
233 	string_stream_set_append_newlines(stream_1, true);
234 	KUNIT_EXPECT_TRUE(test, stream_1->append_newlines);
235 
236 	/* Stream 2 does not append newlines */
237 	stream_2 = kunit_alloc_string_stream(test, GFP_KERNEL);
238 	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream_2);
239 
240 	/* Appending a stream with a newline should not add another newline */
241 	string_stream_add(stream_1, "Original string\n");
242 	string_stream_add(stream_2, "Appended content\n");
243 	string_stream_add(stream_2, "More stuff\n");
244 	string_stream_append(stream_1, stream_2);
245 	KUNIT_EXPECT_STREQ(test, get_concatenated_string(test, stream_1),
246 			   "Original string\nAppended content\nMore stuff\n");
247 
248 	kunit_free_string_stream(test, stream_2);
249 	stream_2 = kunit_alloc_string_stream(test, GFP_KERNEL);
250 	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream_2);
251 
252 	/*
253 	 * Appending a stream without newline should add a final newline.
254 	 * The appended string_stream is treated as a single string so newlines
255 	 * should not be inserted between fragments.
256 	 */
257 	string_stream_add(stream_2, "Another");
258 	string_stream_add(stream_2, "And again");
259 	string_stream_append(stream_1, stream_2);
260 	KUNIT_EXPECT_STREQ(test, get_concatenated_string(test, stream_1),
261 			   "Original string\nAppended content\nMore stuff\nAnotherAnd again\n");
262 }
263 
264 /* Adding an empty string should not create a fragment. */
265 static void string_stream_append_empty_string_test(struct kunit *test)
266 {
267 	struct string_stream *stream;
268 	int original_frag_count;
269 
270 	stream = kunit_alloc_string_stream(test, GFP_KERNEL);
271 	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream);
272 
273 	/* Formatted empty string */
274 	string_stream_add(stream, "%s", "");
275 	KUNIT_EXPECT_TRUE(test, string_stream_is_empty(stream));
276 	KUNIT_EXPECT_TRUE(test, list_empty(&stream->fragments));
277 
278 	/* Adding an empty string to a non-empty stream */
279 	string_stream_add(stream, "Add this line");
280 	original_frag_count = list_count_nodes(&stream->fragments);
281 
282 	string_stream_add(stream, "%s", "");
283 	KUNIT_EXPECT_EQ(test, list_count_nodes(&stream->fragments), original_frag_count);
284 	KUNIT_EXPECT_STREQ(test, get_concatenated_string(test, stream), "Add this line");
285 }
286 
287 /* Adding strings without automatic newline appending */
288 static void string_stream_no_auto_newline_test(struct kunit *test)
289 {
290 	struct string_stream *stream;
291 
292 	stream = kunit_alloc_string_stream(test, GFP_KERNEL);
293 	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream);
294 
295 	/*
296 	 * Add some strings with and without newlines. All formatted newlines
297 	 * should be preserved. It should not add any extra newlines.
298 	 */
299 	string_stream_add(stream, "One");
300 	string_stream_add(stream, "Two\n");
301 	string_stream_add(stream, "%s\n", "Three");
302 	string_stream_add(stream, "%s", "Four\n");
303 	string_stream_add(stream, "Five\n%s", "Six");
304 	string_stream_add(stream, "Seven\n\n");
305 	string_stream_add(stream, "Eight");
306 	KUNIT_EXPECT_STREQ(test, get_concatenated_string(test, stream),
307 			   "OneTwo\nThree\nFour\nFive\nSixSeven\n\nEight");
308 }
309 
310 /* Adding strings with automatic newline appending */
311 static void string_stream_auto_newline_test(struct kunit *test)
312 {
313 	struct string_stream *stream;
314 
315 	stream = kunit_alloc_string_stream(test, GFP_KERNEL);
316 	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream);
317 
318 	string_stream_set_append_newlines(stream, true);
319 	KUNIT_EXPECT_TRUE(test, stream->append_newlines);
320 
321 	/*
322 	 * Add some strings with and without newlines. Newlines should
323 	 * be appended to lines that do not end with \n, but newlines
324 	 * resulting from the formatting should not be changed.
325 	 */
326 	string_stream_add(stream, "One");
327 	string_stream_add(stream, "Two\n");
328 	string_stream_add(stream, "%s\n", "Three");
329 	string_stream_add(stream, "%s", "Four\n");
330 	string_stream_add(stream, "Five\n%s", "Six");
331 	string_stream_add(stream, "Seven\n\n");
332 	string_stream_add(stream, "Eight");
333 	KUNIT_EXPECT_STREQ(test, get_concatenated_string(test, stream),
334 			   "One\nTwo\nThree\nFour\nFive\nSix\nSeven\n\nEight\n");
335 }
336 
337 static struct kunit_case string_stream_test_cases[] = {
338 	KUNIT_CASE(string_stream_init_test),
339 	KUNIT_CASE(string_stream_line_add_test),
340 	KUNIT_CASE(string_stream_variable_length_line_test),
341 	KUNIT_CASE(string_stream_append_test),
342 	KUNIT_CASE(string_stream_append_auto_newline_test),
343 	KUNIT_CASE(string_stream_append_empty_string_test),
344 	KUNIT_CASE(string_stream_no_auto_newline_test),
345 	KUNIT_CASE(string_stream_auto_newline_test),
346 	{}
347 };
348 
349 static struct kunit_suite string_stream_test_suite = {
350 	.name = "string-stream-test",
351 	.test_cases = string_stream_test_cases
352 };
353 kunit_test_suites(&string_stream_test_suite);
354