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