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