1 /*- 2 * BSD LICENSE 3 * 4 * Copyright (c) Intel Corporation. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * * Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * * Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in 15 * the documentation and/or other materials provided with the 16 * distribution. 17 * * Neither the name of Intel Corporation nor the names of its 18 * contributors may be used to endorse or promote products derived 19 * from this software without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 24 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 25 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 26 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 27 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 28 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 29 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 31 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 */ 33 34 #include "spdk/stdinc.h" 35 36 #include "CUnit/Basic.h" 37 #include "spdk_cunit.h" 38 39 #include "spdk/util.h" 40 41 #include "scsi/dev.c" 42 #include "scsi/port.c" 43 44 #include "spdk_internal/mock.h" 45 46 static char *g_bdev_names[] = { 47 "malloc0", 48 "malloc1", 49 }; 50 51 static struct spdk_scsi_port *g_initiator_port_with_pending_tasks = NULL; 52 static struct spdk_scsi_port *g_initiator_port_with_pending_mgmt_tasks = NULL; 53 54 static struct spdk_scsi_task * 55 spdk_get_task(uint32_t *owner_task_ctr) 56 { 57 struct spdk_scsi_task *task; 58 59 task = calloc(1, sizeof(*task)); 60 if (!task) { 61 return NULL; 62 } 63 64 return task; 65 } 66 67 void 68 spdk_scsi_task_put(struct spdk_scsi_task *task) 69 { 70 free(task); 71 } 72 73 struct spdk_scsi_lun *scsi_lun_construct(const char *bdev_name, 74 void (*resize_cb)(const struct spdk_scsi_lun *, void *), 75 void *resize_ctx, 76 void (*hotremove_cb)(const struct spdk_scsi_lun *, void *), 77 void *hotremove_ctx) 78 { 79 struct spdk_scsi_lun *lun; 80 size_t i; 81 82 for (i = 0; i < SPDK_COUNTOF(g_bdev_names); i++) { 83 if (strcmp(bdev_name, g_bdev_names[i]) == 0) { 84 lun = calloc(1, sizeof(struct spdk_scsi_lun)); 85 SPDK_CU_ASSERT_FATAL(lun != NULL); 86 87 return lun; 88 } 89 } 90 91 return NULL; 92 } 93 94 void 95 scsi_lun_destruct(struct spdk_scsi_lun *lun) 96 { 97 free(lun); 98 } 99 100 DEFINE_STUB_V(scsi_lun_execute_mgmt_task, 101 (struct spdk_scsi_lun *lun, struct spdk_scsi_task *task)); 102 103 DEFINE_STUB_V(scsi_lun_execute_task, 104 (struct spdk_scsi_lun *lun, struct spdk_scsi_task *task)); 105 106 DEFINE_STUB(scsi_lun_allocate_io_channel, int, 107 (struct spdk_scsi_lun *lun), 0); 108 109 DEFINE_STUB_V(scsi_lun_free_io_channel, (struct spdk_scsi_lun *lun)); 110 111 bool 112 scsi_lun_has_pending_mgmt_tasks(const struct spdk_scsi_lun *lun, 113 const struct spdk_scsi_port *initiator_port) 114 { 115 return (g_initiator_port_with_pending_mgmt_tasks == initiator_port); 116 } 117 118 bool 119 scsi_lun_has_pending_tasks(const struct spdk_scsi_lun *lun, 120 const struct spdk_scsi_port *initiator_port) 121 { 122 return (g_initiator_port_with_pending_tasks == initiator_port); 123 } 124 125 static void 126 dev_destruct_null_dev(void) 127 { 128 /* pass null for the dev */ 129 spdk_scsi_dev_destruct(NULL, NULL, NULL); 130 } 131 132 static void 133 dev_destruct_zero_luns(void) 134 { 135 struct spdk_scsi_dev dev = { .is_allocated = 1 }; 136 137 /* No luns attached to the dev */ 138 139 /* free the dev */ 140 spdk_scsi_dev_destruct(&dev, NULL, NULL); 141 } 142 143 static void 144 dev_destruct_null_lun(void) 145 { 146 struct spdk_scsi_dev dev = { .is_allocated = 1 }; 147 148 /* pass null for the lun */ 149 dev.lun[0] = NULL; 150 151 /* free the dev */ 152 spdk_scsi_dev_destruct(&dev, NULL, NULL); 153 } 154 155 static void 156 dev_destruct_success(void) 157 { 158 struct spdk_scsi_dev dev = { .is_allocated = 1 }; 159 int rc; 160 161 /* dev with a single lun */ 162 rc = spdk_scsi_dev_add_lun(&dev, "malloc0", 0, NULL, NULL); 163 164 CU_ASSERT(rc == 0); 165 166 /* free the dev */ 167 spdk_scsi_dev_destruct(&dev, NULL, NULL); 168 169 } 170 171 static void 172 dev_construct_num_luns_zero(void) 173 { 174 struct spdk_scsi_dev *dev; 175 const char *bdev_name_list[1] = {}; 176 int lun_id_list[1] = { 0 }; 177 178 dev = spdk_scsi_dev_construct("Name", bdev_name_list, lun_id_list, 0, 179 SPDK_SPC_PROTOCOL_IDENTIFIER_ISCSI, NULL, NULL); 180 181 /* dev should be null since we passed num_luns = 0 */ 182 CU_ASSERT_TRUE(dev == NULL); 183 } 184 185 static void 186 dev_construct_no_lun_zero(void) 187 { 188 struct spdk_scsi_dev *dev; 189 const char *bdev_name_list[1] = {}; 190 int lun_id_list[1] = { 0 }; 191 192 lun_id_list[0] = 1; 193 194 dev = spdk_scsi_dev_construct("Name", bdev_name_list, lun_id_list, 1, 195 SPDK_SPC_PROTOCOL_IDENTIFIER_ISCSI, NULL, NULL); 196 197 /* dev should be null since no LUN0 was specified (lun_id_list[0] = 1) */ 198 CU_ASSERT_TRUE(dev == NULL); 199 } 200 201 static void 202 dev_construct_null_lun(void) 203 { 204 struct spdk_scsi_dev *dev; 205 const char *bdev_name_list[1] = {}; 206 int lun_id_list[1] = { 0 }; 207 208 dev = spdk_scsi_dev_construct("Name", bdev_name_list, lun_id_list, 1, 209 SPDK_SPC_PROTOCOL_IDENTIFIER_ISCSI, NULL, NULL); 210 211 /* dev should be null since no LUN0 was specified (lun_list[0] = NULL) */ 212 CU_ASSERT_TRUE(dev == NULL); 213 } 214 215 static void 216 dev_construct_name_too_long(void) 217 { 218 struct spdk_scsi_dev *dev; 219 const char *bdev_name_list[1] = {"malloc0"}; 220 int lun_id_list[1] = { 0 }; 221 char name[SPDK_SCSI_DEV_MAX_NAME + 1 + 1]; 222 223 /* Try to construct a dev with a name that is one byte longer than allowed. */ 224 memset(name, 'x', sizeof(name) - 1); 225 name[sizeof(name) - 1] = '\0'; 226 227 dev = spdk_scsi_dev_construct(name, bdev_name_list, lun_id_list, 1, 228 SPDK_SPC_PROTOCOL_IDENTIFIER_ISCSI, NULL, NULL); 229 230 CU_ASSERT(dev == NULL); 231 } 232 233 static void 234 dev_construct_success(void) 235 { 236 struct spdk_scsi_dev *dev; 237 const char *bdev_name_list[1] = {"malloc0"}; 238 int lun_id_list[1] = { 0 }; 239 240 dev = spdk_scsi_dev_construct("Name", bdev_name_list, lun_id_list, 1, 241 SPDK_SPC_PROTOCOL_IDENTIFIER_ISCSI, NULL, NULL); 242 243 /* Successfully constructs and returns a dev */ 244 CU_ASSERT_TRUE(dev != NULL); 245 246 /* free the dev */ 247 spdk_scsi_dev_destruct(dev, NULL, NULL); 248 } 249 250 static void 251 dev_construct_success_lun_zero_not_first(void) 252 { 253 struct spdk_scsi_dev *dev; 254 const char *bdev_name_list[2] = {"malloc1", "malloc0"}; 255 int lun_id_list[2] = { 1, 0 }; 256 257 dev = spdk_scsi_dev_construct("Name", bdev_name_list, lun_id_list, 2, 258 SPDK_SPC_PROTOCOL_IDENTIFIER_ISCSI, NULL, NULL); 259 260 /* Successfully constructs and returns a dev */ 261 CU_ASSERT_TRUE(dev != NULL); 262 263 /* free the dev */ 264 spdk_scsi_dev_destruct(dev, NULL, NULL); 265 } 266 267 static void 268 dev_queue_mgmt_task_success(void) 269 { 270 struct spdk_scsi_dev *dev; 271 const char *bdev_name_list[1] = {"malloc0"}; 272 int lun_id_list[1] = { 0 }; 273 struct spdk_scsi_task *task; 274 275 dev = spdk_scsi_dev_construct("Name", bdev_name_list, lun_id_list, 1, 276 SPDK_SPC_PROTOCOL_IDENTIFIER_ISCSI, NULL, NULL); 277 278 /* Successfully constructs and returns a dev */ 279 CU_ASSERT_TRUE(dev != NULL); 280 281 task = spdk_get_task(NULL); 282 283 task->function = SPDK_SCSI_TASK_FUNC_LUN_RESET; 284 spdk_scsi_dev_queue_mgmt_task(dev, task); 285 286 spdk_scsi_task_put(task); 287 288 spdk_scsi_dev_destruct(dev, NULL, NULL); 289 } 290 291 static void 292 dev_queue_task_success(void) 293 { 294 struct spdk_scsi_dev *dev; 295 const char *bdev_name_list[1] = {"malloc0"}; 296 int lun_id_list[1] = { 0 }; 297 struct spdk_scsi_task *task; 298 299 dev = spdk_scsi_dev_construct("Name", bdev_name_list, lun_id_list, 1, 300 SPDK_SPC_PROTOCOL_IDENTIFIER_ISCSI, NULL, NULL); 301 302 /* Successfully constructs and returns a dev */ 303 CU_ASSERT_TRUE(dev != NULL); 304 305 task = spdk_get_task(NULL); 306 307 spdk_scsi_dev_queue_task(dev, task); 308 309 spdk_scsi_task_put(task); 310 311 spdk_scsi_dev_destruct(dev, NULL, NULL); 312 } 313 314 static void 315 dev_stop_success(void) 316 { 317 struct spdk_scsi_dev dev = { 0 }; 318 struct spdk_scsi_task *task; 319 struct spdk_scsi_task *task_mgmt; 320 321 task = spdk_get_task(NULL); 322 323 spdk_scsi_dev_queue_task(&dev, task); 324 325 task_mgmt = spdk_get_task(NULL); 326 327 /* Enqueue the tasks into dev->task_mgmt_submit_queue */ 328 task->function = SPDK_SCSI_TASK_FUNC_LUN_RESET; 329 spdk_scsi_dev_queue_mgmt_task(&dev, task_mgmt); 330 331 spdk_scsi_task_put(task); 332 spdk_scsi_task_put(task_mgmt); 333 } 334 335 static void 336 dev_add_port_max_ports(void) 337 { 338 struct spdk_scsi_dev dev = { 0 }; 339 const char *name; 340 int id, rc; 341 342 /* dev is set to SPDK_SCSI_DEV_MAX_PORTS */ 343 dev.num_ports = SPDK_SCSI_DEV_MAX_PORTS; 344 name = "Name of Port"; 345 id = 1; 346 347 rc = spdk_scsi_dev_add_port(&dev, id, name); 348 349 /* returns -1; since the dev already has maximum 350 * number of ports (SPDK_SCSI_DEV_MAX_PORTS) */ 351 CU_ASSERT_TRUE(rc < 0); 352 } 353 354 static void 355 dev_add_port_construct_failure1(void) 356 { 357 struct spdk_scsi_dev dev = { 0 }; 358 const int port_name_length = SPDK_SCSI_PORT_MAX_NAME_LENGTH + 2; 359 char name[port_name_length]; 360 uint64_t id; 361 int rc; 362 363 dev.num_ports = 1; 364 /* Set the name such that the length exceeds SPDK_SCSI_PORT_MAX_NAME_LENGTH 365 * SPDK_SCSI_PORT_MAX_NAME_LENGTH = 256 */ 366 memset(name, 'a', port_name_length - 1); 367 name[port_name_length - 1] = '\0'; 368 id = 1; 369 370 rc = spdk_scsi_dev_add_port(&dev, id, name); 371 372 /* returns -1; since the length of the name exceeds 373 * SPDK_SCSI_PORT_MAX_NAME_LENGTH */ 374 CU_ASSERT_TRUE(rc < 0); 375 } 376 377 static void 378 dev_add_port_construct_failure2(void) 379 { 380 struct spdk_scsi_dev dev = { 0 }; 381 const char *name; 382 uint64_t id; 383 int rc; 384 385 dev.num_ports = 1; 386 name = "Name of Port"; 387 id = 1; 388 389 /* Initialize port[0] to be valid and its index is set to 1 */ 390 dev.port[0].id = id; 391 dev.port[0].is_used = 1; 392 393 rc = spdk_scsi_dev_add_port(&dev, id, name); 394 395 /* returns -1; since the dev already has a port whose index to be 1 */ 396 CU_ASSERT_TRUE(rc < 0); 397 } 398 399 static void 400 dev_add_port_success1(void) 401 { 402 struct spdk_scsi_dev dev = { 0 }; 403 const char *name; 404 int id, rc; 405 406 dev.num_ports = 1; 407 name = "Name of Port"; 408 id = 1; 409 410 rc = spdk_scsi_dev_add_port(&dev, id, name); 411 412 /* successfully adds a port */ 413 CU_ASSERT_EQUAL(rc, 0); 414 /* Assert num_ports has been incremented to 2 */ 415 CU_ASSERT_EQUAL(dev.num_ports, 2); 416 } 417 418 static void 419 dev_add_port_success2(void) 420 { 421 struct spdk_scsi_dev dev = { 0 }; 422 const char *name; 423 uint64_t id; 424 int rc; 425 426 dev.num_ports = 1; 427 name = "Name of Port"; 428 id = 1; 429 /* set id of invalid port[0] to 1. This must be ignored */ 430 dev.port[0].id = id; 431 dev.port[0].is_used = 0; 432 433 rc = spdk_scsi_dev_add_port(&dev, id, name); 434 435 /* successfully adds a port */ 436 CU_ASSERT_EQUAL(rc, 0); 437 /* Assert num_ports has been incremented to 1 */ 438 CU_ASSERT_EQUAL(dev.num_ports, 2); 439 } 440 441 static void 442 dev_add_port_success3(void) 443 { 444 struct spdk_scsi_dev dev = { 0 }; 445 const char *name; 446 uint64_t add_id; 447 int rc; 448 449 dev.num_ports = 1; 450 name = "Name of Port"; 451 dev.port[0].id = 1; 452 dev.port[0].is_used = 1; 453 add_id = 2; 454 455 /* Add a port with id = 2 */ 456 rc = spdk_scsi_dev_add_port(&dev, add_id, name); 457 458 /* successfully adds a port */ 459 CU_ASSERT_EQUAL(rc, 0); 460 /* Assert num_ports has been incremented to 2 */ 461 CU_ASSERT_EQUAL(dev.num_ports, 2); 462 } 463 464 static void 465 dev_find_port_by_id_num_ports_zero(void) 466 { 467 struct spdk_scsi_dev dev = { 0 }; 468 struct spdk_scsi_port *rp_port; 469 uint64_t id; 470 471 dev.num_ports = 0; 472 id = 1; 473 474 rp_port = spdk_scsi_dev_find_port_by_id(&dev, id); 475 476 /* returns null; since dev's num_ports is 0 */ 477 CU_ASSERT_TRUE(rp_port == NULL); 478 } 479 480 static void 481 dev_find_port_by_id_id_not_found_failure(void) 482 { 483 struct spdk_scsi_dev dev = { 0 }; 484 struct spdk_scsi_port *rp_port; 485 const char *name; 486 int rc; 487 uint64_t id, find_id; 488 489 id = 1; 490 dev.num_ports = 1; 491 name = "Name of Port"; 492 find_id = 2; 493 494 /* Add a port with id = 1 */ 495 rc = spdk_scsi_dev_add_port(&dev, id, name); 496 497 CU_ASSERT_EQUAL(rc, 0); 498 499 /* Find port with id = 2 */ 500 rp_port = spdk_scsi_dev_find_port_by_id(&dev, find_id); 501 502 /* returns null; failed to find port specified by id = 2 */ 503 CU_ASSERT_TRUE(rp_port == NULL); 504 } 505 506 static void 507 dev_find_port_by_id_success(void) 508 { 509 struct spdk_scsi_dev dev = { 0 }; 510 struct spdk_scsi_port *rp_port; 511 const char *name; 512 int rc; 513 uint64_t id; 514 515 id = 1; 516 dev.num_ports = 1; 517 name = "Name of Port"; 518 519 /* Add a port */ 520 rc = spdk_scsi_dev_add_port(&dev, id, name); 521 522 CU_ASSERT_EQUAL(rc, 0); 523 524 /* Find port by the same id as the one added above */ 525 rp_port = spdk_scsi_dev_find_port_by_id(&dev, id); 526 527 /* Successfully found port specified by id */ 528 CU_ASSERT_TRUE(rp_port != NULL); 529 if (rp_port != NULL) { 530 /* Assert the found port's id and name are same as 531 * the port added. */ 532 CU_ASSERT_EQUAL(rp_port->id, 1); 533 CU_ASSERT_STRING_EQUAL(rp_port->name, "Name of Port"); 534 } 535 } 536 537 static void 538 dev_add_lun_bdev_not_found(void) 539 { 540 int rc; 541 struct spdk_scsi_dev dev = {0}; 542 543 rc = spdk_scsi_dev_add_lun(&dev, "malloc2", 0, NULL, NULL); 544 545 SPDK_CU_ASSERT_FATAL(dev.lun[0] == NULL); 546 CU_ASSERT_NOT_EQUAL(rc, 0); 547 } 548 549 static void 550 dev_add_lun_no_free_lun_id(void) 551 { 552 int rc; 553 int i; 554 struct spdk_scsi_dev dev = {0}; 555 struct spdk_scsi_lun lun; 556 557 for (i = 0; i < SPDK_SCSI_DEV_MAX_LUN; i++) { 558 dev.lun[i] = &lun; 559 } 560 561 rc = spdk_scsi_dev_add_lun(&dev, "malloc0", -1, NULL, NULL); 562 563 CU_ASSERT_NOT_EQUAL(rc, 0); 564 } 565 566 static void 567 dev_add_lun_success1(void) 568 { 569 int rc; 570 struct spdk_scsi_dev dev = {0}; 571 572 rc = spdk_scsi_dev_add_lun(&dev, "malloc0", -1, NULL, NULL); 573 574 CU_ASSERT_EQUAL(rc, 0); 575 576 spdk_scsi_dev_destruct(&dev, NULL, NULL); 577 } 578 579 static void 580 dev_add_lun_success2(void) 581 { 582 int rc; 583 struct spdk_scsi_dev dev = {0}; 584 585 rc = spdk_scsi_dev_add_lun(&dev, "malloc0", 0, NULL, NULL); 586 587 CU_ASSERT_EQUAL(rc, 0); 588 589 spdk_scsi_dev_destruct(&dev, NULL, NULL); 590 } 591 592 static void 593 dev_check_pending_tasks(void) 594 { 595 struct spdk_scsi_dev dev = {}; 596 struct spdk_scsi_lun lun = {}; 597 struct spdk_scsi_port initiator_port = {}; 598 599 g_initiator_port_with_pending_tasks = NULL; 600 g_initiator_port_with_pending_mgmt_tasks = NULL; 601 602 CU_ASSERT(spdk_scsi_dev_has_pending_tasks(&dev, NULL) == false); 603 604 dev.lun[SPDK_SCSI_DEV_MAX_LUN - 1] = &lun; 605 606 CU_ASSERT(spdk_scsi_dev_has_pending_tasks(&dev, NULL) == true); 607 CU_ASSERT(spdk_scsi_dev_has_pending_tasks(&dev, &initiator_port) == false); 608 609 g_initiator_port_with_pending_tasks = &initiator_port; 610 CU_ASSERT(spdk_scsi_dev_has_pending_tasks(&dev, NULL) == true); 611 CU_ASSERT(spdk_scsi_dev_has_pending_tasks(&dev, &initiator_port) == true); 612 613 g_initiator_port_with_pending_tasks = NULL; 614 g_initiator_port_with_pending_mgmt_tasks = &initiator_port; 615 CU_ASSERT(spdk_scsi_dev_has_pending_tasks(&dev, NULL) == true); 616 CU_ASSERT(spdk_scsi_dev_has_pending_tasks(&dev, &initiator_port) == true); 617 } 618 619 int 620 main(int argc, char **argv) 621 { 622 CU_pSuite suite = NULL; 623 unsigned int num_failures; 624 625 CU_set_error_action(CUEA_ABORT); 626 CU_initialize_registry(); 627 628 suite = CU_add_suite("dev_suite", NULL, NULL); 629 630 CU_ADD_TEST(suite, dev_destruct_null_dev); 631 CU_ADD_TEST(suite, dev_destruct_zero_luns); 632 CU_ADD_TEST(suite, dev_destruct_null_lun); 633 CU_ADD_TEST(suite, dev_destruct_success); 634 CU_ADD_TEST(suite, dev_construct_num_luns_zero); 635 CU_ADD_TEST(suite, dev_construct_no_lun_zero); 636 CU_ADD_TEST(suite, dev_construct_null_lun); 637 CU_ADD_TEST(suite, dev_construct_name_too_long); 638 CU_ADD_TEST(suite, dev_construct_success); 639 CU_ADD_TEST(suite, dev_construct_success_lun_zero_not_first); 640 CU_ADD_TEST(suite, dev_queue_mgmt_task_success); 641 CU_ADD_TEST(suite, dev_queue_task_success); 642 CU_ADD_TEST(suite, dev_stop_success); 643 CU_ADD_TEST(suite, dev_add_port_max_ports); 644 CU_ADD_TEST(suite, dev_add_port_construct_failure1); 645 CU_ADD_TEST(suite, dev_add_port_construct_failure2); 646 CU_ADD_TEST(suite, dev_add_port_success1); 647 CU_ADD_TEST(suite, dev_add_port_success2); 648 CU_ADD_TEST(suite, dev_add_port_success3); 649 CU_ADD_TEST(suite, dev_find_port_by_id_num_ports_zero); 650 CU_ADD_TEST(suite, dev_find_port_by_id_id_not_found_failure); 651 CU_ADD_TEST(suite, dev_find_port_by_id_success); 652 CU_ADD_TEST(suite, dev_add_lun_bdev_not_found); 653 CU_ADD_TEST(suite, dev_add_lun_no_free_lun_id); 654 CU_ADD_TEST(suite, dev_add_lun_success1); 655 CU_ADD_TEST(suite, dev_add_lun_success2); 656 CU_ADD_TEST(suite, dev_check_pending_tasks); 657 658 CU_basic_set_mode(CU_BRM_VERBOSE); 659 CU_basic_run_tests(); 660 num_failures = CU_get_number_of_failures(); 661 CU_cleanup_registry(); 662 663 return num_failures; 664 } 665