1 /* SPDX-License-Identifier: BSD-3-Clause 2 * Copyright (c) Intel Corporation. 3 * All rights reserved. 4 */ 5 6 #include <sys/queue.h> 7 8 #include "spdk/stdinc.h" 9 10 #include "spdk_cunit.h" 11 #include "common/lib/test_env.c" 12 13 #include "ftl/mngt/ftl_mngt.c" 14 15 #define CALLER_CB_RET_VALUE 999 16 17 /* list for structure with results of tests from callbacks */ 18 struct entry { 19 int data; 20 TAILQ_ENTRY(entry) entries; 21 }; 22 23 TAILQ_HEAD(listhead, entry) g_head; 24 25 struct thread_send_msg_container { 26 spdk_msg_fn fn; 27 void *ctx; 28 } g_thread_send_msg_container; 29 30 struct spdk_ftl_dev g_dev; 31 32 int 33 spdk_thread_send_msg(const struct spdk_thread *thread, spdk_msg_fn fn, void *ctx) 34 { 35 g_thread_send_msg_container.fn = fn; 36 g_thread_send_msg_container.ctx = ctx; 37 return 0; 38 } 39 40 struct spdk_thread * 41 spdk_get_thread(void) 42 { 43 struct spdk_thread *thd = (struct spdk_thread *)0x1; 44 return thd; 45 } 46 47 static int 48 setup_test_list(void) 49 { 50 TAILQ_INIT(&g_head); 51 return 0; 52 } 53 54 static void 55 check_list_empty(void) 56 { 57 CU_ASSERT_TRUE(TAILQ_EMPTY(&g_head)); 58 } 59 60 static void 61 add_elem_to_test_list(int data) 62 { 63 struct entry *en = calloc(1, sizeof(*en)); 64 SPDK_CU_ASSERT_FATAL(en != NULL); 65 en->data = data; 66 TAILQ_INSERT_TAIL(&g_head, en, entries); 67 } 68 69 static void 70 check_elem_on_list_and_remove(int compared_elem) 71 { 72 struct entry *en = TAILQ_FIRST(&g_head); 73 if (en != NULL) { 74 CU_ASSERT_EQUAL(en->data, compared_elem); 75 TAILQ_REMOVE(&g_head, en, entries); 76 free(en); 77 } else { 78 CU_FAIL("not null value was expected"); 79 } 80 } 81 82 static void 83 fn_finish(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt) 84 { 85 add_elem_to_test_list(CALLER_CB_RET_VALUE); 86 g_thread_send_msg_container.fn = NULL; 87 g_thread_send_msg_container.ctx = NULL; 88 } 89 90 typedef int (*ftl_execute_fn)(struct spdk_ftl_dev *dev, 91 const struct ftl_mngt_process_desc *process, 92 ftl_mngt_fn cb, void *cb_cntx); 93 94 static void 95 run_ftl_mngt_with_cb_cntx(ftl_execute_fn exec_fn, 96 const struct ftl_mngt_process_desc *process, void *cb_cntx) 97 { 98 int result = exec_fn(&g_dev, process, fn_finish, cb_cntx); 99 CU_ASSERT_EQUAL(result, 0); 100 while (g_thread_send_msg_container.fn != NULL) { 101 g_thread_send_msg_container.fn(g_thread_send_msg_container.ctx); 102 } 103 } 104 105 static void 106 run_ftl_mngt(ftl_execute_fn exec_fn, 107 const struct ftl_mngt_process_desc *process) 108 { 109 run_ftl_mngt_with_cb_cntx(exec_fn, process, NULL); 110 } 111 112 /*- 113 * test 1 114 * tests simple invoking next steps 115 * it is shown if ftl_mngt_process_execute and ftl_mngt_process_rollback invoke functions in proper order 116 * (functions call only ftl_mngt_next_step) 117 */ 118 119 static void 120 fn_1_1_action(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt) 121 { 122 add_elem_to_test_list(1); 123 ftl_mngt_next_step(mngt); 124 } 125 126 static void 127 fn_1_1_cleanup(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt) 128 { 129 add_elem_to_test_list(-1); 130 ftl_mngt_next_step(mngt); 131 } 132 133 static void 134 fn_1_2_action(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt) 135 { 136 add_elem_to_test_list(2); 137 ftl_mngt_next_step(mngt); 138 } 139 140 static void 141 fn_1_3_action(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt) 142 { 143 add_elem_to_test_list(3); 144 ftl_mngt_next_step(mngt); 145 } 146 147 static void 148 fn_1_3_cleanup(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt) 149 { 150 add_elem_to_test_list(-3); 151 ftl_mngt_next_step(mngt); 152 } 153 154 static struct ftl_mngt_process_desc pdesc_test_1 = { 155 .name = "process 1", 156 .steps = { 157 { 158 .name = "step 1", 159 .action = fn_1_1_action, 160 .cleanup = fn_1_1_cleanup 161 }, 162 { 163 .name = "step 2", 164 .action = fn_1_2_action 165 }, 166 { 167 .name = "step 3", 168 .action = fn_1_3_action, 169 .cleanup = fn_1_3_cleanup 170 }, 171 {} 172 } 173 }; 174 175 static void 176 test_next_step(void) 177 { 178 run_ftl_mngt(ftl_mngt_process_execute, &pdesc_test_1); 179 180 /* check proper order of action functions */ 181 for (int i = 1; i <= 3; i++) { 182 check_elem_on_list_and_remove(i); 183 } 184 185 /* check if caller callback was invoked */ 186 check_elem_on_list_and_remove(CALLER_CB_RET_VALUE); 187 188 run_ftl_mngt(ftl_mngt_process_rollback, &pdesc_test_1); 189 190 /* Check proper order of cleanup functions. 191 * Cleanup functions add to list opposite values to action functions. 192 * Cleanup functions are invoked in reverse order, 193 * moreover action 2 does not have cleanup, 194 * so expected values are -3, then -1 */ 195 check_elem_on_list_and_remove(-3); 196 check_elem_on_list_and_remove(-1); 197 198 /* check if caller callback was invoked */ 199 check_elem_on_list_and_remove(CALLER_CB_RET_VALUE); 200 201 check_list_empty(); 202 } 203 204 /*- 205 * test 2 206 * tests action and cleanup function which invoke 207 * ftl_mngt_continue_step function 208 */ 209 210 static void 211 fn_2_common_part(struct ftl_mngt_process *mngt, int elem) 212 { 213 struct entry *en = TAILQ_LAST(&g_head, listhead); 214 215 if (en == NULL || en->data != elem) { 216 /* if function was invoked 1st time, make it once again */ 217 add_elem_to_test_list(elem); 218 ftl_mngt_continue_step(mngt); 219 } else { 220 /* otherwise go to the next function */ 221 add_elem_to_test_list(elem); 222 ftl_mngt_next_step(mngt); 223 } 224 } 225 226 static void 227 fn_2_1_action(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt) 228 { 229 fn_2_common_part(mngt, 1); 230 } 231 232 static void 233 fn_2_1_cleanup(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt) 234 { 235 fn_2_common_part(mngt, -1); 236 } 237 238 static void 239 fn_2_2_action(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt) 240 { 241 fn_2_common_part(mngt, 2); 242 } 243 244 static void 245 fn_2_2_cleanup(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt) 246 { 247 fn_2_common_part(mngt, -2); 248 } 249 250 static struct ftl_mngt_process_desc pdesc_test_2 = { 251 .name = "process 2", 252 .steps = { 253 { 254 .name = "step 1", 255 .action = fn_2_1_action, 256 .cleanup = fn_2_1_cleanup 257 }, 258 { 259 .name = "step 2", 260 .action = fn_2_2_action, 261 .cleanup = fn_2_2_cleanup 262 }, 263 {} 264 } 265 }; 266 267 static void 268 test_continue_step(void) 269 { 270 run_ftl_mngt(ftl_mngt_process_execute, &pdesc_test_2); 271 272 /* check proper order of action functions */ 273 check_elem_on_list_and_remove(1); 274 check_elem_on_list_and_remove(1); 275 check_elem_on_list_and_remove(2); 276 check_elem_on_list_and_remove(2); 277 278 /* check if caller callback was invoked */ 279 check_elem_on_list_and_remove(CALLER_CB_RET_VALUE); 280 281 run_ftl_mngt(ftl_mngt_process_rollback, &pdesc_test_2); 282 283 /* check proper order of action functions */ 284 check_elem_on_list_and_remove(-2); 285 check_elem_on_list_and_remove(-2); 286 check_elem_on_list_and_remove(-1); 287 check_elem_on_list_and_remove(-1); 288 289 /* check if caller callback was invoked */ 290 check_elem_on_list_and_remove(CALLER_CB_RET_VALUE); 291 292 check_list_empty(); 293 } 294 295 /*- 296 * test 3 297 * tests ftl_mngt_alloc_step_cntx and all ftl_mngt_get functions 298 */ 299 300 const int PROCESS_CNTX_TEST_VAL_0 = 21; 301 const int PROCESS_CNTX_TEST_VAL_1 = 37; 302 const int STEP_CNTX_TEST_VAL = 1; 303 304 static void 305 put_on_list(void) 306 { 307 struct entry *en = calloc(1, sizeof(*en)); 308 SPDK_CU_ASSERT_FATAL(en != NULL); 309 TAILQ_INSERT_TAIL(&g_head, en, entries); 310 } 311 312 static bool 313 check_if_list_empty_and_clean(void) 314 { 315 struct entry *en = TAILQ_FIRST(&g_head); 316 if (en == NULL) { 317 return true; 318 } else { 319 TAILQ_REMOVE(&g_head, en, entries); 320 free(en); 321 return false; 322 } 323 } 324 325 static void 326 fn_3_1_action(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt) 327 { 328 int *step_cntx_ptr, *process_cntx_ptr; 329 char *caller_cntx_ptr; 330 int status; 331 332 step_cntx_ptr = ftl_mngt_get_step_ctx(mngt); 333 if (check_if_list_empty_and_clean()) { 334 /* In 1st run of this function test list is empty 335 * and 'if' is true, this part of function is done. 336 * That 'if' part ends with ftl_mngt_continue_step, 337 * so function will be called once again. 338 * Element is added to the test list 339 * to invoke 'else' in second run */ 340 put_on_list(); 341 /* this step descriptor does not locate any context 342 * at the beginning, 343 * so pointer should contain NULL */ 344 CU_ASSERT_PTR_NULL(step_cntx_ptr); 345 346 status = ftl_mngt_alloc_step_ctx(mngt, sizeof(*step_cntx_ptr)); 347 SPDK_CU_ASSERT_FATAL(status == 0); 348 step_cntx_ptr = ftl_mngt_get_step_ctx(mngt); 349 /* now pointer should point to allocated context */ 350 CU_ASSERT_PTR_NOT_NULL(step_cntx_ptr); 351 352 /* this value should be retrieved in second run of function 353 * (in 'else' part) */ 354 *step_cntx_ptr = STEP_CNTX_TEST_VAL; 355 356 ftl_mngt_continue_step(mngt); 357 } else { 358 /* In second run retrieved pointer is not empty. 359 * Moreover it should contain value allocated for this step 360 * in previous run of function */ 361 CU_ASSERT_PTR_NOT_NULL(step_cntx_ptr); 362 CU_ASSERT_EQUAL(*step_cntx_ptr, STEP_CNTX_TEST_VAL); 363 364 /* check getting device */ 365 CU_ASSERT_EQUAL(ftl_mngt_get_dev(mngt), dev); 366 CU_ASSERT_EQUAL(ftl_mngt_get_dev(mngt), &g_dev); 367 368 /* tests for process context */ 369 process_cntx_ptr = ftl_mngt_get_process_ctx(mngt); 370 371 /* 1st get of process context, should be clear ('0' values) */ 372 CU_ASSERT_EQUAL(process_cntx_ptr[0], 0); 373 CU_ASSERT_EQUAL(process_cntx_ptr[1], 0); 374 375 /* Random values put in process context. 376 * Should be retrieved in the next function 377 * (it is common space for the entire process) */ 378 process_cntx_ptr[0] = PROCESS_CNTX_TEST_VAL_0; 379 process_cntx_ptr[1] = PROCESS_CNTX_TEST_VAL_1; 380 381 /* tests for caller context */ 382 caller_cntx_ptr = ftl_mngt_get_caller_ctx(mngt); 383 384 /* check previously located values */ 385 CU_ASSERT_EQUAL(caller_cntx_ptr[0], 'd'); 386 CU_ASSERT_EQUAL(caller_cntx_ptr[1], 'a'); 387 CU_ASSERT_EQUAL(caller_cntx_ptr[2], 'j'); 388 389 /* insert new */ 390 caller_cntx_ptr[0] = ' '; 391 caller_cntx_ptr[1] = 'k'; 392 caller_cntx_ptr[2] = 'a'; 393 394 ftl_mngt_next_step(mngt); 395 } 396 } 397 398 static void 399 fn_3_2_action(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt) 400 { 401 int *step_cntx_ptr, *process_cntx_ptr; 402 char *caller_cntx_ptr; 403 int status; 404 405 step_cntx_ptr = ftl_mngt_get_step_ctx(mngt); 406 /* context of this step descriptor is never empty 407 * so pointer cannot contain NULL */ 408 CU_ASSERT_PTR_NOT_NULL(step_cntx_ptr); 409 410 if (check_if_list_empty_and_clean()) { 411 /* In 1st run of this function test list is empty 412 * and 'if' is true, this part of function is done. 413 * That 'if' part ends with ftl_mngt_continue_step, 414 * so function will be called once again. 415 * Element is added to the test list 416 * to invoke 'else' in second run */ 417 put_on_list(); 418 419 /* check getting device */ 420 CU_ASSERT_EQUAL(ftl_mngt_get_dev(mngt), dev); 421 CU_ASSERT_EQUAL(ftl_mngt_get_dev(mngt), &g_dev); 422 423 /* tests for process context */ 424 process_cntx_ptr = ftl_mngt_get_process_ctx(mngt); 425 426 /* check if it is possible to retrieve values located 427 * in process context by previous function */ 428 CU_ASSERT_EQUAL(process_cntx_ptr[0], PROCESS_CNTX_TEST_VAL_0); 429 CU_ASSERT_EQUAL(process_cntx_ptr[1], PROCESS_CNTX_TEST_VAL_1); 430 431 /* tests for caller context */ 432 caller_cntx_ptr = ftl_mngt_get_caller_ctx(mngt); 433 434 /* check previously located values */ 435 CU_ASSERT_EQUAL(caller_cntx_ptr[0], ' '); 436 CU_ASSERT_EQUAL(caller_cntx_ptr[1], 'k'); 437 CU_ASSERT_EQUAL(caller_cntx_ptr[2], 'a'); 438 439 /* insert new */ 440 caller_cntx_ptr[0] = 'm'; 441 caller_cntx_ptr[1] = 'i'; 442 caller_cntx_ptr[2] = 'e'; 443 444 /* first run of step so reserved step context 445 * was never used before and should contain 0 */ 446 CU_ASSERT_EQUAL(*step_cntx_ptr, 0); 447 448 /* this value should be retrieved in second run of function 449 * (in 'else' part) */ 450 *step_cntx_ptr = STEP_CNTX_TEST_VAL; 451 452 ftl_mngt_continue_step(mngt); 453 } else { 454 /* In second run retrieved pointer should contain value 455 * allocated for this step in previous run of function */ 456 CU_ASSERT_EQUAL(*step_cntx_ptr, STEP_CNTX_TEST_VAL); 457 458 status = ftl_mngt_alloc_step_ctx(mngt, sizeof(*step_cntx_ptr)); 459 SPDK_CU_ASSERT_FATAL(status == 0); 460 step_cntx_ptr = ftl_mngt_get_step_ctx(mngt); 461 462 /* now pointer should point to newly allocated context 463 * and be cleaned up (should contain '0') */ 464 CU_ASSERT_PTR_NOT_NULL(step_cntx_ptr); 465 CU_ASSERT_EQUAL(*step_cntx_ptr, 0); 466 467 ftl_mngt_next_step(mngt); 468 } 469 } 470 471 static void 472 fn_3_2_cleanup(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt) 473 { 474 int *step_cntx_ptr, *process_cntx_ptr; 475 char *caller_cntx_ptr; 476 int status; 477 478 step_cntx_ptr = ftl_mngt_get_step_ctx(mngt); 479 /* context of this step descriptor is never empty 480 * so pointer cannot contain NULL */ 481 CU_ASSERT_PTR_NOT_NULL(step_cntx_ptr); 482 483 if (check_if_list_empty_and_clean()) { 484 /* In 1st run of this function test list is empty 485 * and 'if' is true, this part of function is done. 486 * That 'if' part ends with ftl_mngt_continue_step, 487 * so function will be called once again. 488 * Element is added to the test list 489 * to invoke 'else' in second run */ 490 put_on_list(); 491 492 /* first run of step so reserved step context 493 * was never used before and should contain 0 */ 494 CU_ASSERT_EQUAL(*step_cntx_ptr, 0); 495 496 /* this value should be retrieved in second run of function 497 * (in 'else' part) */ 498 *step_cntx_ptr = STEP_CNTX_TEST_VAL; 499 500 ftl_mngt_continue_step(mngt); 501 } else { 502 /* In second run retrieved pointer should contain value 503 * allocated for this step in previous run of function */ 504 CU_ASSERT_EQUAL(*step_cntx_ptr, STEP_CNTX_TEST_VAL); 505 506 status = ftl_mngt_alloc_step_ctx(mngt, sizeof(*step_cntx_ptr)); 507 SPDK_CU_ASSERT_FATAL(status == 0); 508 step_cntx_ptr = ftl_mngt_get_step_ctx(mngt); 509 510 /* now pointer should point to newly allocated context 511 * and be cleaned up (should contain '0') */ 512 CU_ASSERT_PTR_NOT_NULL(step_cntx_ptr); 513 CU_ASSERT_EQUAL(*step_cntx_ptr, 0); 514 515 /* check getting device */ 516 CU_ASSERT_EQUAL(ftl_mngt_get_dev(mngt), dev); 517 CU_ASSERT_EQUAL(ftl_mngt_get_dev(mngt), &g_dev); 518 519 /* tests for process context */ 520 process_cntx_ptr = ftl_mngt_get_process_ctx(mngt); 521 522 /* 1st get of process context, should be clear ('0' values) */ 523 CU_ASSERT_EQUAL(process_cntx_ptr[0], 0); 524 CU_ASSERT_EQUAL(process_cntx_ptr[1], 0); 525 526 /* Random values put in process context. 527 * Should be retrieved in the next function 528 * (it is common space for the entire process) */ 529 process_cntx_ptr[0] = PROCESS_CNTX_TEST_VAL_0; 530 process_cntx_ptr[1] = PROCESS_CNTX_TEST_VAL_1; 531 532 /* tests for caller context */ 533 caller_cntx_ptr = ftl_mngt_get_caller_ctx(mngt); 534 535 /* check previously located values */ 536 CU_ASSERT_EQUAL(caller_cntx_ptr[0], 'm'); 537 CU_ASSERT_EQUAL(caller_cntx_ptr[1], 'i'); 538 CU_ASSERT_EQUAL(caller_cntx_ptr[2], 'e'); 539 540 /* insert new */ 541 caller_cntx_ptr[0] = 'n'; 542 caller_cntx_ptr[1] = 'i'; 543 caller_cntx_ptr[2] = 'a'; 544 545 ftl_mngt_next_step(mngt); 546 } 547 } 548 549 static void 550 fn_3_1_cleanup(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt) 551 { 552 int *step_cntx_ptr, *process_cntx_ptr; 553 char *caller_cntx_ptr; 554 int status; 555 556 step_cntx_ptr = ftl_mngt_get_step_ctx(mngt); 557 if (check_if_list_empty_and_clean()) { 558 /* In 1st run of this function test list is empty 559 * and 'if' is true, this part of function is done. 560 * That 'if' part ends with ftl_mngt_continue_step, 561 * so function will be called once again. 562 * Element is added to the test list 563 * to invoke 'else' in second run */ 564 put_on_list(); 565 /* this step descriptor does not locate any context 566 * at the beginning, 567 * so pointer should contain NULL */ 568 CU_ASSERT_PTR_NULL(step_cntx_ptr); 569 570 /* check getting device */ 571 CU_ASSERT_EQUAL(ftl_mngt_get_dev(mngt), dev); 572 CU_ASSERT_EQUAL(ftl_mngt_get_dev(mngt), &g_dev); 573 574 /* tests for process context */ 575 process_cntx_ptr = ftl_mngt_get_process_ctx(mngt); 576 577 /* check if it is possible to retrieve values located 578 * in process context by previous function */ 579 CU_ASSERT_EQUAL(process_cntx_ptr[0], PROCESS_CNTX_TEST_VAL_0); 580 CU_ASSERT_EQUAL(process_cntx_ptr[1], PROCESS_CNTX_TEST_VAL_1); 581 582 /* tests for caller context */ 583 caller_cntx_ptr = ftl_mngt_get_caller_ctx(mngt); 584 585 /* check previously located values */ 586 CU_ASSERT_EQUAL(caller_cntx_ptr[0], 'n'); 587 CU_ASSERT_EQUAL(caller_cntx_ptr[1], 'i'); 588 CU_ASSERT_EQUAL(caller_cntx_ptr[2], 'a'); 589 590 /* insert new */ 591 caller_cntx_ptr[0] = '!'; 592 caller_cntx_ptr[1] = '!'; 593 caller_cntx_ptr[2] = '!'; 594 595 status = ftl_mngt_alloc_step_ctx(mngt, sizeof(*step_cntx_ptr)); 596 SPDK_CU_ASSERT_FATAL(status == 0); 597 step_cntx_ptr = ftl_mngt_get_step_ctx(mngt); 598 /* now pointer should point to allocated context */ 599 CU_ASSERT_PTR_NOT_NULL(step_cntx_ptr); 600 601 /* this value should be retrieved in second run of function 602 * (in 'else' part) */ 603 *step_cntx_ptr = STEP_CNTX_TEST_VAL; 604 605 ftl_mngt_continue_step(mngt); 606 } else { 607 /* In second run retrieved pointer is not empty. 608 * Moreover it should contain value allocated for this step 609 * in previous run of function */ 610 CU_ASSERT_PTR_NOT_NULL(step_cntx_ptr); 611 CU_ASSERT_EQUAL(*step_cntx_ptr, STEP_CNTX_TEST_VAL); 612 613 ftl_mngt_next_step(mngt); 614 } 615 } 616 617 static struct ftl_mngt_process_desc pdesc_test_3 = { 618 .name = "process 3", 619 .ctx_size = 2 * sizeof(int), 620 .steps = { 621 { 622 .name = "step 1", 623 .action = fn_3_1_action, 624 .cleanup = fn_3_1_cleanup 625 }, 626 { 627 .name = "step 2", 628 .ctx_size = sizeof(int), 629 .action = fn_3_2_action, 630 .cleanup = fn_3_2_cleanup 631 }, 632 {} 633 } 634 }; 635 636 static void 637 test_get_func_and_step_cntx_alloc(void) 638 { 639 char cb_cntx[4] = "daj"; 640 641 run_ftl_mngt_with_cb_cntx(ftl_mngt_process_execute, &pdesc_test_3, cb_cntx); 642 643 /* check if caller callback was invoked */ 644 check_elem_on_list_and_remove(CALLER_CB_RET_VALUE); 645 646 /* check if steps changed cb_cntx correctly */ 647 CU_ASSERT_EQUAL(cb_cntx[0], 'm'); 648 CU_ASSERT_EQUAL(cb_cntx[1], 'i'); 649 CU_ASSERT_EQUAL(cb_cntx[2], 'e'); 650 651 run_ftl_mngt_with_cb_cntx(ftl_mngt_process_rollback, &pdesc_test_3, cb_cntx); 652 653 /* check if caller callback was invoked */ 654 check_elem_on_list_and_remove(CALLER_CB_RET_VALUE); 655 656 /* check if steps changed cb_cntx correctly */ 657 CU_ASSERT_EQUAL(cb_cntx[0], '!'); 658 CU_ASSERT_EQUAL(cb_cntx[1], '!'); 659 CU_ASSERT_EQUAL(cb_cntx[2], '!'); 660 661 check_list_empty(); 662 } 663 664 665 666 /*- 667 * test 4 668 * tests ftl_mngt_fail_step function 669 * 670 * In that test one of the action functions fails (third one). 671 * Because of that expected result (saved on the test result list) 672 * are numbers of the next action function up to failing function. 673 * After that cleanup functions are invoked in reversed order. 674 */ 675 676 static void 677 fn_4_1_action(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt) 678 { 679 add_elem_to_test_list(1); 680 ftl_mngt_next_step(mngt); 681 } 682 683 static void 684 fn_4_1_cleanup(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt) 685 { 686 add_elem_to_test_list(-1); 687 ftl_mngt_next_step(mngt); 688 } 689 690 static void 691 fn_4_2_action(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt) 692 { 693 add_elem_to_test_list(2); 694 ftl_mngt_next_step(mngt); 695 } 696 697 static void 698 fn_4_2_cleanup(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt) 699 { 700 add_elem_to_test_list(-2); 701 ftl_mngt_next_step(mngt); 702 } 703 704 static void 705 fn_4_3_action(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt) 706 { 707 add_elem_to_test_list(3); 708 /* this action fails, so cleanup should begin now */ 709 ftl_mngt_fail_step(mngt); 710 } 711 712 static void 713 fn_4_3_cleanup(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt) 714 { 715 add_elem_to_test_list(-3); 716 ftl_mngt_next_step(mngt); 717 } 718 719 static void 720 fn_4_4_action(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt) 721 { 722 CU_FAIL("failure cannot start another action"); 723 ftl_mngt_next_step(mngt); 724 } 725 726 static struct ftl_mngt_process_desc pdesc_test_4 = { 727 .name = "process 4", 728 .steps = { 729 { 730 .name = "step 1", 731 .action = fn_4_1_action, 732 .cleanup = fn_4_1_cleanup 733 }, 734 { 735 .name = "step 2", 736 .action = fn_4_2_action, 737 .cleanup = fn_4_2_cleanup 738 }, 739 { 740 .name = "step 3", 741 .action = fn_4_3_action, 742 .cleanup = fn_4_3_cleanup 743 }, 744 { 745 .name = "step 2", 746 .action = fn_4_4_action 747 }, 748 {} 749 } 750 }; 751 752 static void 753 test_fail_step(void) 754 { 755 run_ftl_mngt(ftl_mngt_process_execute, &pdesc_test_4); 756 757 /* check proper order of action functions */ 758 for (int i = 1; i <= 3; i++) { 759 check_elem_on_list_and_remove(i); 760 } 761 762 /* 3rd action function fails, so now should be 763 * cleanup functions in reverse order */ 764 for (int i = 3; i > 0; i--) { 765 check_elem_on_list_and_remove(-i); 766 } 767 768 /* check if caller callback was invoked */ 769 check_elem_on_list_and_remove(CALLER_CB_RET_VALUE); 770 771 check_list_empty(); 772 } 773 774 /*- 775 * test 5 776 * tests ftl_mngt_call_process and ftl_mngt_call_process_rollback functions 777 * tests only proper flow without failures 778 */ 779 780 static void 781 fn_5_2_1_action(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt) 782 { 783 add_elem_to_test_list(21); 784 ftl_mngt_next_step(mngt); 785 } 786 787 static void 788 fn_5_2_1_cleanup(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt) 789 { 790 add_elem_to_test_list(-21); 791 ftl_mngt_next_step(mngt); 792 } 793 794 static void 795 fn_5_2_2_action(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt) 796 { 797 add_elem_to_test_list(22); 798 ftl_mngt_next_step(mngt); 799 } 800 801 static void 802 fn_5_2_2_cleanup(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt) 803 { 804 add_elem_to_test_list(-22); 805 ftl_mngt_next_step(mngt); 806 } 807 808 static struct ftl_mngt_process_desc pdesc_test_5_2 = { 809 .name = "process nested inside step 2 from process 5", 810 .steps = { 811 { 812 .name = "step 2_1", 813 .action = fn_5_2_1_action, 814 .cleanup = fn_5_2_1_cleanup 815 }, 816 { 817 .name = "step 2_2", 818 .action = fn_5_2_2_action, 819 .cleanup = fn_5_2_2_cleanup 820 }, 821 {} 822 } 823 }; 824 825 static void 826 fn_5_3_1_action(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt) 827 { 828 add_elem_to_test_list(31); 829 ftl_mngt_next_step(mngt); 830 } 831 832 static void 833 fn_5_3_1_cleanup(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt) 834 { 835 add_elem_to_test_list(-31); 836 ftl_mngt_next_step(mngt); 837 } 838 839 static void 840 fn_5_3_2_action(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt) 841 { 842 add_elem_to_test_list(32); 843 ftl_mngt_next_step(mngt); 844 } 845 846 static void 847 fn_5_3_2_cleanup(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt) 848 { 849 add_elem_to_test_list(-32); 850 ftl_mngt_next_step(mngt); 851 } 852 853 static struct ftl_mngt_process_desc pdesc_test_5_3 = { 854 .name = "process nested inside step 2 from process 5", 855 .steps = { 856 { 857 .name = "step 3_1", 858 .action = fn_5_3_1_action, 859 .cleanup = fn_5_3_1_cleanup 860 }, 861 { 862 .name = "step 3_2", 863 .action = fn_5_3_2_action, 864 .cleanup = fn_5_3_2_cleanup 865 }, 866 {} 867 } 868 }; 869 870 static void 871 fn_5_1_action(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt) 872 { 873 add_elem_to_test_list(1); 874 ftl_mngt_next_step(mngt); 875 } 876 877 static void 878 fn_5_1_cleanup(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt) 879 { 880 add_elem_to_test_list(-1); 881 ftl_mngt_next_step(mngt); 882 } 883 884 static void 885 fn_5_2_action(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt) 886 { 887 add_elem_to_test_list(2); 888 ftl_mngt_call_process(mngt, &pdesc_test_5_2); 889 } 890 891 static void 892 fn_5_2_cleanup(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt) 893 { 894 add_elem_to_test_list(-2); 895 ftl_mngt_call_process_rollback(mngt, &pdesc_test_5_2); 896 } 897 898 static void 899 fn_5_3_action(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt) 900 { 901 add_elem_to_test_list(3); 902 ftl_mngt_call_process_rollback(mngt, &pdesc_test_5_3); 903 } 904 905 static void 906 fn_5_3_cleanup(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt) 907 { 908 add_elem_to_test_list(-3); 909 ftl_mngt_call_process(mngt, &pdesc_test_5_3); 910 } 911 912 static struct ftl_mngt_process_desc pdesc_test_5 = { 913 .name = "process 5 main", 914 .steps = { 915 { 916 .name = "step 1", 917 .action = fn_5_1_action, 918 .cleanup = fn_5_1_cleanup 919 }, 920 { 921 .name = "step 2", 922 .action = fn_5_2_action, 923 .cleanup = fn_5_2_cleanup 924 }, 925 { 926 .name = "step 3", 927 .action = fn_5_3_action, 928 .cleanup = fn_5_3_cleanup 929 }, 930 {} 931 } 932 }; 933 934 static void 935 test_mngt_call_and_call_rollback(void) 936 { 937 run_ftl_mngt(ftl_mngt_process_execute, &pdesc_test_5); 938 939 check_elem_on_list_and_remove(1); 940 check_elem_on_list_and_remove(2); 941 check_elem_on_list_and_remove(21); 942 check_elem_on_list_and_remove(22); 943 check_elem_on_list_and_remove(3); 944 check_elem_on_list_and_remove(-32); 945 check_elem_on_list_and_remove(-31); 946 947 /* check if caller callback was invoked */ 948 check_elem_on_list_and_remove(CALLER_CB_RET_VALUE); 949 950 run_ftl_mngt(ftl_mngt_process_rollback, &pdesc_test_5); 951 952 check_elem_on_list_and_remove(-3); 953 check_elem_on_list_and_remove(31); 954 check_elem_on_list_and_remove(32); 955 check_elem_on_list_and_remove(-2); 956 check_elem_on_list_and_remove(-22); 957 check_elem_on_list_and_remove(-21); 958 check_elem_on_list_and_remove(-1); 959 960 /* check if caller callback was invoked */ 961 check_elem_on_list_and_remove(CALLER_CB_RET_VALUE); 962 963 check_list_empty(); 964 } 965 966 /* 967 * test 6 968 * tests failure inside nested process 969 */ 970 971 static void 972 fn_6_2_1_action(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt) 973 { 974 add_elem_to_test_list(21); 975 ftl_mngt_next_step(mngt); 976 } 977 978 static void 979 fn_6_2_1_cleanup(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt) 980 { 981 add_elem_to_test_list(-21); 982 ftl_mngt_next_step(mngt); 983 } 984 985 static void 986 fn_6_2_2_action(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt) 987 { 988 add_elem_to_test_list(22); 989 /* this action fails, so cleanup should begin now */ 990 ftl_mngt_fail_step(mngt); 991 } 992 993 static void 994 fn_6_2_3_action(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt) 995 { 996 CU_FAIL("failure cannot start another action"); 997 ftl_mngt_next_step(mngt); 998 } 999 1000 static struct ftl_mngt_process_desc pdesc_test_6_2 = { 1001 .name = "process nested inside step 2 from process 6", 1002 .steps = { 1003 { 1004 .name = "step 6_1", 1005 .action = fn_6_2_1_action, 1006 .cleanup = fn_6_2_1_cleanup 1007 }, 1008 { 1009 .name = "step 6_2", 1010 .action = fn_6_2_2_action 1011 }, 1012 { 1013 .name = "step 6_3", 1014 .action = fn_6_2_3_action 1015 }, 1016 {} 1017 } 1018 }; 1019 1020 static void 1021 fn_6_1_action(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt) 1022 { 1023 add_elem_to_test_list(1); 1024 ftl_mngt_next_step(mngt); 1025 } 1026 1027 static void 1028 fn_6_2_action(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt) 1029 { 1030 add_elem_to_test_list(2); 1031 ftl_mngt_call_process(mngt, &pdesc_test_6_2); 1032 } 1033 1034 static void 1035 fn_6_2_cleanup(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt) 1036 { 1037 add_elem_to_test_list(-2); 1038 ftl_mngt_next_step(mngt); 1039 } 1040 1041 static void 1042 fn_6_3_action(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt) 1043 { 1044 CU_FAIL("failure cannot start another action"); 1045 ftl_mngt_next_step(mngt); 1046 } 1047 1048 static struct ftl_mngt_process_desc pdesc_test_6 = { 1049 .name = "process 6 main", 1050 .steps = { 1051 { 1052 .name = "step 1", 1053 .action = fn_6_1_action 1054 }, 1055 { 1056 .name = "step 2", 1057 .action = fn_6_2_action, 1058 .cleanup = fn_6_2_cleanup 1059 }, 1060 { 1061 .name = "step 3", 1062 .action = fn_6_3_action 1063 }, 1064 {} 1065 } 1066 }; 1067 1068 static void 1069 test_nested_process_failure(void) 1070 { 1071 run_ftl_mngt(ftl_mngt_process_execute, &pdesc_test_6); 1072 1073 check_elem_on_list_and_remove(1); 1074 check_elem_on_list_and_remove(2); 1075 check_elem_on_list_and_remove(21); 1076 check_elem_on_list_and_remove(22); 1077 check_elem_on_list_and_remove(-21); 1078 check_elem_on_list_and_remove(-2); 1079 1080 /* check if caller callback was invoked */ 1081 check_elem_on_list_and_remove(CALLER_CB_RET_VALUE); 1082 1083 check_list_empty(); 1084 } 1085 1086 int 1087 main(int argc, char **argv) 1088 { 1089 CU_pSuite suite = NULL; 1090 unsigned int num_failures; 1091 1092 CU_set_error_action(CUEA_ABORT); 1093 CU_initialize_registry(); 1094 1095 suite = CU_add_suite("ftl_mngt", setup_test_list, NULL); 1096 1097 CU_ADD_TEST(suite, test_next_step); 1098 CU_ADD_TEST(suite, test_continue_step); 1099 CU_ADD_TEST(suite, test_get_func_and_step_cntx_alloc); 1100 CU_ADD_TEST(suite, test_fail_step); 1101 CU_ADD_TEST(suite, test_mngt_call_and_call_rollback); 1102 CU_ADD_TEST(suite, test_nested_process_failure); 1103 1104 CU_basic_set_mode(CU_BRM_VERBOSE); 1105 CU_basic_run_tests(); 1106 num_failures = CU_get_number_of_failures(); 1107 CU_cleanup_registry(); 1108 1109 return num_failures; 1110 } 1111