kunit-test.c (13924B)
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * KUnit test for core test infrastructure. 4 * 5 * Copyright (C) 2019, Google LLC. 6 * Author: Brendan Higgins <brendanhiggins@google.com> 7 */ 8#include <kunit/test.h> 9 10#include "try-catch-impl.h" 11 12struct kunit_try_catch_test_context { 13 struct kunit_try_catch *try_catch; 14 bool function_called; 15}; 16 17static void kunit_test_successful_try(void *data) 18{ 19 struct kunit *test = data; 20 struct kunit_try_catch_test_context *ctx = test->priv; 21 22 ctx->function_called = true; 23} 24 25static void kunit_test_no_catch(void *data) 26{ 27 struct kunit *test = data; 28 29 KUNIT_FAIL(test, "Catch should not be called\n"); 30} 31 32static void kunit_test_try_catch_successful_try_no_catch(struct kunit *test) 33{ 34 struct kunit_try_catch_test_context *ctx = test->priv; 35 struct kunit_try_catch *try_catch = ctx->try_catch; 36 37 kunit_try_catch_init(try_catch, 38 test, 39 kunit_test_successful_try, 40 kunit_test_no_catch); 41 kunit_try_catch_run(try_catch, test); 42 43 KUNIT_EXPECT_TRUE(test, ctx->function_called); 44} 45 46static void kunit_test_unsuccessful_try(void *data) 47{ 48 struct kunit *test = data; 49 struct kunit_try_catch_test_context *ctx = test->priv; 50 struct kunit_try_catch *try_catch = ctx->try_catch; 51 52 kunit_try_catch_throw(try_catch); 53 KUNIT_FAIL(test, "This line should never be reached\n"); 54} 55 56static void kunit_test_catch(void *data) 57{ 58 struct kunit *test = data; 59 struct kunit_try_catch_test_context *ctx = test->priv; 60 61 ctx->function_called = true; 62} 63 64static void kunit_test_try_catch_unsuccessful_try_does_catch(struct kunit *test) 65{ 66 struct kunit_try_catch_test_context *ctx = test->priv; 67 struct kunit_try_catch *try_catch = ctx->try_catch; 68 69 kunit_try_catch_init(try_catch, 70 test, 71 kunit_test_unsuccessful_try, 72 kunit_test_catch); 73 kunit_try_catch_run(try_catch, test); 74 75 KUNIT_EXPECT_TRUE(test, ctx->function_called); 76} 77 78static int kunit_try_catch_test_init(struct kunit *test) 79{ 80 struct kunit_try_catch_test_context *ctx; 81 82 ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); 83 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); 84 test->priv = ctx; 85 86 ctx->try_catch = kunit_kmalloc(test, 87 sizeof(*ctx->try_catch), 88 GFP_KERNEL); 89 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx->try_catch); 90 91 return 0; 92} 93 94static struct kunit_case kunit_try_catch_test_cases[] = { 95 KUNIT_CASE(kunit_test_try_catch_successful_try_no_catch), 96 KUNIT_CASE(kunit_test_try_catch_unsuccessful_try_does_catch), 97 {} 98}; 99 100static struct kunit_suite kunit_try_catch_test_suite = { 101 .name = "kunit-try-catch-test", 102 .init = kunit_try_catch_test_init, 103 .test_cases = kunit_try_catch_test_cases, 104}; 105 106/* 107 * Context for testing test managed resources 108 * is_resource_initialized is used to test arbitrary resources 109 */ 110struct kunit_test_resource_context { 111 struct kunit test; 112 bool is_resource_initialized; 113 int allocate_order[2]; 114 int free_order[2]; 115}; 116 117static int fake_resource_init(struct kunit_resource *res, void *context) 118{ 119 struct kunit_test_resource_context *ctx = context; 120 121 res->data = &ctx->is_resource_initialized; 122 ctx->is_resource_initialized = true; 123 return 0; 124} 125 126static void fake_resource_free(struct kunit_resource *res) 127{ 128 bool *is_resource_initialized = res->data; 129 130 *is_resource_initialized = false; 131} 132 133static void kunit_resource_test_init_resources(struct kunit *test) 134{ 135 struct kunit_test_resource_context *ctx = test->priv; 136 137 kunit_init_test(&ctx->test, "testing_test_init_test", NULL); 138 139 KUNIT_EXPECT_TRUE(test, list_empty(&ctx->test.resources)); 140} 141 142static void kunit_resource_test_alloc_resource(struct kunit *test) 143{ 144 struct kunit_test_resource_context *ctx = test->priv; 145 struct kunit_resource *res; 146 kunit_resource_free_t free = fake_resource_free; 147 148 res = kunit_alloc_and_get_resource(&ctx->test, 149 fake_resource_init, 150 fake_resource_free, 151 GFP_KERNEL, 152 ctx); 153 154 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, res); 155 KUNIT_EXPECT_PTR_EQ(test, 156 &ctx->is_resource_initialized, 157 (bool *)res->data); 158 KUNIT_EXPECT_TRUE(test, list_is_last(&res->node, &ctx->test.resources)); 159 KUNIT_EXPECT_PTR_EQ(test, free, res->free); 160 161 kunit_put_resource(res); 162} 163 164/* 165 * Note: tests below use kunit_alloc_and_get_resource(), so as a consequence 166 * they have a reference to the associated resource that they must release 167 * via kunit_put_resource(). In normal operation, users will only 168 * have to do this for cases where they use kunit_find_resource(), and the 169 * kunit_alloc_resource() function will be used (which does not take a 170 * resource reference). 171 */ 172static void kunit_resource_test_destroy_resource(struct kunit *test) 173{ 174 struct kunit_test_resource_context *ctx = test->priv; 175 struct kunit_resource *res = kunit_alloc_and_get_resource( 176 &ctx->test, 177 fake_resource_init, 178 fake_resource_free, 179 GFP_KERNEL, 180 ctx); 181 182 kunit_put_resource(res); 183 184 KUNIT_ASSERT_FALSE(test, 185 kunit_destroy_resource(&ctx->test, 186 kunit_resource_instance_match, 187 res->data)); 188 189 KUNIT_EXPECT_FALSE(test, ctx->is_resource_initialized); 190 KUNIT_EXPECT_TRUE(test, list_empty(&ctx->test.resources)); 191} 192 193static void kunit_resource_test_remove_resource(struct kunit *test) 194{ 195 struct kunit_test_resource_context *ctx = test->priv; 196 struct kunit_resource *res = kunit_alloc_and_get_resource( 197 &ctx->test, 198 fake_resource_init, 199 fake_resource_free, 200 GFP_KERNEL, 201 ctx); 202 203 /* The resource is in the list */ 204 KUNIT_EXPECT_FALSE(test, list_empty(&ctx->test.resources)); 205 206 /* Remove the resource. The pointer is still valid, but it can't be 207 * found. 208 */ 209 kunit_remove_resource(test, res); 210 KUNIT_EXPECT_TRUE(test, list_empty(&ctx->test.resources)); 211 /* We haven't been freed yet. */ 212 KUNIT_EXPECT_TRUE(test, ctx->is_resource_initialized); 213 214 /* Removing the resource multiple times is valid. */ 215 kunit_remove_resource(test, res); 216 KUNIT_EXPECT_TRUE(test, list_empty(&ctx->test.resources)); 217 /* Despite having been removed twice (from only one reference), the 218 * resource still has not been freed. 219 */ 220 KUNIT_EXPECT_TRUE(test, ctx->is_resource_initialized); 221 222 /* Free the resource. */ 223 kunit_put_resource(res); 224 KUNIT_EXPECT_FALSE(test, ctx->is_resource_initialized); 225} 226 227static void kunit_resource_test_cleanup_resources(struct kunit *test) 228{ 229 int i; 230 struct kunit_test_resource_context *ctx = test->priv; 231 struct kunit_resource *resources[5]; 232 233 for (i = 0; i < ARRAY_SIZE(resources); i++) { 234 resources[i] = kunit_alloc_and_get_resource(&ctx->test, 235 fake_resource_init, 236 fake_resource_free, 237 GFP_KERNEL, 238 ctx); 239 kunit_put_resource(resources[i]); 240 } 241 242 kunit_cleanup(&ctx->test); 243 244 KUNIT_EXPECT_TRUE(test, list_empty(&ctx->test.resources)); 245} 246 247static void kunit_resource_test_mark_order(int order_array[], 248 size_t order_size, 249 int key) 250{ 251 int i; 252 253 for (i = 0; i < order_size && order_array[i]; i++) 254 ; 255 256 order_array[i] = key; 257} 258 259#define KUNIT_RESOURCE_TEST_MARK_ORDER(ctx, order_field, key) \ 260 kunit_resource_test_mark_order(ctx->order_field, \ 261 ARRAY_SIZE(ctx->order_field), \ 262 key) 263 264static int fake_resource_2_init(struct kunit_resource *res, void *context) 265{ 266 struct kunit_test_resource_context *ctx = context; 267 268 KUNIT_RESOURCE_TEST_MARK_ORDER(ctx, allocate_order, 2); 269 270 res->data = ctx; 271 272 return 0; 273} 274 275static void fake_resource_2_free(struct kunit_resource *res) 276{ 277 struct kunit_test_resource_context *ctx = res->data; 278 279 KUNIT_RESOURCE_TEST_MARK_ORDER(ctx, free_order, 2); 280} 281 282static int fake_resource_1_init(struct kunit_resource *res, void *context) 283{ 284 struct kunit_test_resource_context *ctx = context; 285 struct kunit_resource *res2; 286 287 res2 = kunit_alloc_and_get_resource(&ctx->test, 288 fake_resource_2_init, 289 fake_resource_2_free, 290 GFP_KERNEL, 291 ctx); 292 293 KUNIT_RESOURCE_TEST_MARK_ORDER(ctx, allocate_order, 1); 294 295 res->data = ctx; 296 297 kunit_put_resource(res2); 298 299 return 0; 300} 301 302static void fake_resource_1_free(struct kunit_resource *res) 303{ 304 struct kunit_test_resource_context *ctx = res->data; 305 306 KUNIT_RESOURCE_TEST_MARK_ORDER(ctx, free_order, 1); 307} 308 309/* 310 * TODO(brendanhiggins@google.com): replace the arrays that keep track of the 311 * order of allocation and freeing with strict mocks using the IN_SEQUENCE macro 312 * to assert allocation and freeing order when the feature becomes available. 313 */ 314static void kunit_resource_test_proper_free_ordering(struct kunit *test) 315{ 316 struct kunit_test_resource_context *ctx = test->priv; 317 struct kunit_resource *res; 318 319 /* fake_resource_1 allocates a fake_resource_2 in its init. */ 320 res = kunit_alloc_and_get_resource(&ctx->test, 321 fake_resource_1_init, 322 fake_resource_1_free, 323 GFP_KERNEL, 324 ctx); 325 326 /* 327 * Since fake_resource_2_init calls KUNIT_RESOURCE_TEST_MARK_ORDER 328 * before returning to fake_resource_1_init, it should be the first to 329 * put its key in the allocate_order array. 330 */ 331 KUNIT_EXPECT_EQ(test, ctx->allocate_order[0], 2); 332 KUNIT_EXPECT_EQ(test, ctx->allocate_order[1], 1); 333 334 kunit_put_resource(res); 335 336 kunit_cleanup(&ctx->test); 337 338 /* 339 * Because fake_resource_2 finishes allocation before fake_resource_1, 340 * fake_resource_1 should be freed first since it could depend on 341 * fake_resource_2. 342 */ 343 KUNIT_EXPECT_EQ(test, ctx->free_order[0], 1); 344 KUNIT_EXPECT_EQ(test, ctx->free_order[1], 2); 345} 346 347static void kunit_resource_test_static(struct kunit *test) 348{ 349 struct kunit_test_resource_context ctx; 350 struct kunit_resource res; 351 352 KUNIT_EXPECT_EQ(test, kunit_add_resource(test, NULL, NULL, &res, &ctx), 353 0); 354 355 KUNIT_EXPECT_PTR_EQ(test, res.data, (void *)&ctx); 356 357 kunit_cleanup(test); 358 359 KUNIT_EXPECT_TRUE(test, list_empty(&test->resources)); 360} 361 362static void kunit_resource_test_named(struct kunit *test) 363{ 364 struct kunit_resource res1, res2, *found = NULL; 365 struct kunit_test_resource_context ctx; 366 367 KUNIT_EXPECT_EQ(test, 368 kunit_add_named_resource(test, NULL, NULL, &res1, 369 "resource_1", &ctx), 370 0); 371 KUNIT_EXPECT_PTR_EQ(test, res1.data, (void *)&ctx); 372 373 KUNIT_EXPECT_EQ(test, 374 kunit_add_named_resource(test, NULL, NULL, &res1, 375 "resource_1", &ctx), 376 -EEXIST); 377 378 KUNIT_EXPECT_EQ(test, 379 kunit_add_named_resource(test, NULL, NULL, &res2, 380 "resource_2", &ctx), 381 0); 382 383 found = kunit_find_named_resource(test, "resource_1"); 384 385 KUNIT_EXPECT_PTR_EQ(test, found, &res1); 386 387 if (found) 388 kunit_put_resource(&res1); 389 390 KUNIT_EXPECT_EQ(test, kunit_destroy_named_resource(test, "resource_2"), 391 0); 392 393 kunit_cleanup(test); 394 395 KUNIT_EXPECT_TRUE(test, list_empty(&test->resources)); 396} 397 398static int kunit_resource_test_init(struct kunit *test) 399{ 400 struct kunit_test_resource_context *ctx = 401 kzalloc(sizeof(*ctx), GFP_KERNEL); 402 403 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); 404 405 test->priv = ctx; 406 407 kunit_init_test(&ctx->test, "test_test_context", NULL); 408 409 return 0; 410} 411 412static void kunit_resource_test_exit(struct kunit *test) 413{ 414 struct kunit_test_resource_context *ctx = test->priv; 415 416 kunit_cleanup(&ctx->test); 417 kfree(ctx); 418} 419 420static struct kunit_case kunit_resource_test_cases[] = { 421 KUNIT_CASE(kunit_resource_test_init_resources), 422 KUNIT_CASE(kunit_resource_test_alloc_resource), 423 KUNIT_CASE(kunit_resource_test_destroy_resource), 424 KUNIT_CASE(kunit_resource_test_remove_resource), 425 KUNIT_CASE(kunit_resource_test_cleanup_resources), 426 KUNIT_CASE(kunit_resource_test_proper_free_ordering), 427 KUNIT_CASE(kunit_resource_test_static), 428 KUNIT_CASE(kunit_resource_test_named), 429 {} 430}; 431 432static struct kunit_suite kunit_resource_test_suite = { 433 .name = "kunit-resource-test", 434 .init = kunit_resource_test_init, 435 .exit = kunit_resource_test_exit, 436 .test_cases = kunit_resource_test_cases, 437}; 438 439static void kunit_log_test(struct kunit *test); 440 441static struct kunit_case kunit_log_test_cases[] = { 442 KUNIT_CASE(kunit_log_test), 443 {} 444}; 445 446static struct kunit_suite kunit_log_test_suite = { 447 .name = "kunit-log-test", 448 .test_cases = kunit_log_test_cases, 449}; 450 451static void kunit_log_test(struct kunit *test) 452{ 453 struct kunit_suite suite; 454 455 suite.log = kunit_kzalloc(test, KUNIT_LOG_SIZE, GFP_KERNEL); 456 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, suite.log); 457 458 kunit_log(KERN_INFO, test, "put this in log."); 459 kunit_log(KERN_INFO, test, "this too."); 460 kunit_log(KERN_INFO, &suite, "add to suite log."); 461 kunit_log(KERN_INFO, &suite, "along with this."); 462 463#ifdef CONFIG_KUNIT_DEBUGFS 464 KUNIT_EXPECT_NOT_ERR_OR_NULL(test, 465 strstr(test->log, "put this in log.")); 466 KUNIT_EXPECT_NOT_ERR_OR_NULL(test, 467 strstr(test->log, "this too.")); 468 KUNIT_EXPECT_NOT_ERR_OR_NULL(test, 469 strstr(suite.log, "add to suite log.")); 470 KUNIT_EXPECT_NOT_ERR_OR_NULL(test, 471 strstr(suite.log, "along with this.")); 472#else 473 KUNIT_EXPECT_NULL(test, test->log); 474#endif 475} 476 477static void kunit_status_set_failure_test(struct kunit *test) 478{ 479 struct kunit fake; 480 481 kunit_init_test(&fake, "fake test", NULL); 482 483 KUNIT_EXPECT_EQ(test, fake.status, (enum kunit_status)KUNIT_SUCCESS); 484 kunit_set_failure(&fake); 485 KUNIT_EXPECT_EQ(test, fake.status, (enum kunit_status)KUNIT_FAILURE); 486} 487 488static void kunit_status_mark_skipped_test(struct kunit *test) 489{ 490 struct kunit fake; 491 492 kunit_init_test(&fake, "fake test", NULL); 493 494 /* Before: Should be SUCCESS with no comment. */ 495 KUNIT_EXPECT_EQ(test, fake.status, KUNIT_SUCCESS); 496 KUNIT_EXPECT_STREQ(test, fake.status_comment, ""); 497 498 /* Mark the test as skipped. */ 499 kunit_mark_skipped(&fake, "Accepts format string: %s", "YES"); 500 501 /* After: Should be SKIPPED with our comment. */ 502 KUNIT_EXPECT_EQ(test, fake.status, (enum kunit_status)KUNIT_SKIPPED); 503 KUNIT_EXPECT_STREQ(test, fake.status_comment, "Accepts format string: YES"); 504} 505 506static struct kunit_case kunit_status_test_cases[] = { 507 KUNIT_CASE(kunit_status_set_failure_test), 508 KUNIT_CASE(kunit_status_mark_skipped_test), 509 {} 510}; 511 512static struct kunit_suite kunit_status_test_suite = { 513 .name = "kunit_status", 514 .test_cases = kunit_status_test_cases, 515}; 516 517kunit_test_suites(&kunit_try_catch_test_suite, &kunit_resource_test_suite, 518 &kunit_log_test_suite, &kunit_status_test_suite); 519 520MODULE_LICENSE("GPL v2");