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 #include "spdk/util.h" 36 37 #include "spdk_cunit.h" 38 39 #include "sock/sock.c" 40 #include "sock/posix/posix.c" 41 42 #define UT_IP "test_ip" 43 #define UT_PORT 1234 44 45 bool g_read_data_called; 46 ssize_t g_bytes_read; 47 char g_buf[256]; 48 struct spdk_sock *g_server_sock_read; 49 int g_ut_accept_count; 50 struct spdk_ut_sock *g_ut_listen_sock; 51 struct spdk_ut_sock *g_ut_client_sock; 52 53 struct spdk_ut_sock { 54 struct spdk_sock base; 55 struct spdk_ut_sock *peer; 56 size_t bytes_avail; 57 char buf[256]; 58 }; 59 60 struct spdk_ut_sock_group_impl { 61 struct spdk_sock_group_impl base; 62 struct spdk_ut_sock *sock; 63 }; 64 65 #define __ut_sock(sock) (struct spdk_ut_sock *)sock 66 #define __ut_group(group) (struct spdk_ut_sock_group_impl *)group 67 68 static int 69 spdk_ut_sock_getaddr(struct spdk_sock *_sock, char *saddr, int slen, uint16_t *sport, 70 char *caddr, int clen, uint16_t *cport) 71 { 72 return 0; 73 } 74 75 static struct spdk_sock * 76 spdk_ut_sock_listen(const char *ip, int port) 77 { 78 struct spdk_ut_sock *sock; 79 80 if (strcmp(ip, UT_IP) || port != UT_PORT) { 81 return NULL; 82 } 83 84 CU_ASSERT(g_ut_listen_sock == NULL); 85 86 sock = calloc(1, sizeof(*sock)); 87 SPDK_CU_ASSERT_FATAL(sock != NULL); 88 g_ut_listen_sock = sock; 89 90 return &sock->base; 91 } 92 93 static struct spdk_sock * 94 spdk_ut_sock_connect(const char *ip, int port) 95 { 96 struct spdk_ut_sock *sock; 97 98 if (strcmp(ip, UT_IP) || port != UT_PORT) { 99 return NULL; 100 } 101 102 sock = calloc(1, sizeof(*sock)); 103 SPDK_CU_ASSERT_FATAL(sock != NULL); 104 g_ut_accept_count++; 105 CU_ASSERT(g_ut_client_sock == NULL); 106 g_ut_client_sock = sock; 107 108 return &sock->base; 109 } 110 111 static struct spdk_sock * 112 spdk_ut_sock_accept(struct spdk_sock *_sock) 113 { 114 struct spdk_ut_sock *sock = __ut_sock(_sock); 115 struct spdk_ut_sock *new_sock; 116 117 CU_ASSERT(sock == g_ut_listen_sock); 118 119 if (g_ut_accept_count == 0) { 120 errno = EAGAIN; 121 return NULL; 122 } 123 124 g_ut_accept_count--; 125 new_sock = calloc(1, sizeof(*sock)); 126 if (new_sock == NULL) { 127 SPDK_ERRLOG("sock allocation failed\n"); 128 return NULL; 129 } 130 131 SPDK_CU_ASSERT_FATAL(g_ut_client_sock != NULL); 132 g_ut_client_sock->peer = new_sock; 133 134 return &new_sock->base; 135 } 136 137 static int 138 spdk_ut_sock_close(struct spdk_sock *_sock) 139 { 140 struct spdk_ut_sock *sock = __ut_sock(_sock); 141 142 if (sock == g_ut_listen_sock) { 143 g_ut_listen_sock = NULL; 144 } 145 if (sock == g_ut_client_sock) { 146 g_ut_client_sock = NULL; 147 } 148 free(_sock); 149 150 return 0; 151 } 152 153 static ssize_t 154 spdk_ut_sock_recv(struct spdk_sock *_sock, void *buf, size_t len) 155 { 156 struct spdk_ut_sock *sock = __ut_sock(_sock); 157 char tmp[256]; 158 159 len = spdk_min(len, sock->bytes_avail); 160 161 if (len == 0) { 162 errno = EAGAIN; 163 return -1; 164 } 165 166 memcpy(buf, sock->buf, len); 167 memcpy(tmp, &sock->buf[len], sock->bytes_avail - len); 168 memcpy(sock->buf, tmp, sock->bytes_avail - len); 169 sock->bytes_avail -= len; 170 171 return len; 172 } 173 174 static ssize_t 175 spdk_ut_sock_readv(struct spdk_sock *_sock, struct iovec *iov, int iovcnt) 176 { 177 struct spdk_ut_sock *sock = __ut_sock(_sock); 178 size_t len; 179 char tmp[256]; 180 181 /* Test implementation only supports single iov for now. */ 182 CU_ASSERT(iovcnt == 1); 183 184 len = spdk_min(iov[0].iov_len, sock->bytes_avail); 185 186 if (len == 0) { 187 errno = EAGAIN; 188 return -1; 189 } 190 191 memcpy(iov[0].iov_base, sock->buf, len); 192 memcpy(tmp, &sock->buf[len], sock->bytes_avail - len); 193 memcpy(sock->buf, tmp, sock->bytes_avail - len); 194 sock->bytes_avail -= len; 195 196 return len; 197 } 198 199 static ssize_t 200 spdk_ut_sock_writev(struct spdk_sock *_sock, struct iovec *iov, int iovcnt) 201 { 202 struct spdk_ut_sock *sock = __ut_sock(_sock); 203 struct spdk_ut_sock *peer; 204 205 SPDK_CU_ASSERT_FATAL(sock->peer != NULL); 206 peer = sock->peer; 207 208 /* Test implementation only supports single iov for now. */ 209 CU_ASSERT(iovcnt == 1); 210 211 memcpy(&peer->buf[peer->bytes_avail], iov[0].iov_base, iov[0].iov_len); 212 peer->bytes_avail += iov[0].iov_len; 213 214 return iov[0].iov_len; 215 } 216 217 static int 218 spdk_ut_sock_set_recvlowat(struct spdk_sock *_sock, int nbytes) 219 { 220 return 0; 221 } 222 223 static int 224 spdk_ut_sock_set_recvbuf(struct spdk_sock *_sock, int sz) 225 { 226 return 0; 227 } 228 229 static int 230 spdk_ut_sock_set_sendbuf(struct spdk_sock *_sock, int sz) 231 { 232 return 0; 233 } 234 235 static bool 236 spdk_ut_sock_is_ipv6(struct spdk_sock *_sock) 237 { 238 return false; 239 } 240 241 static bool 242 spdk_ut_sock_is_ipv4(struct spdk_sock *_sock) 243 { 244 return true; 245 } 246 247 static int 248 spdk_ut_sock_get_placement_id(struct spdk_sock *_sock, int *placement_id) 249 { 250 return -1; 251 } 252 253 static int 254 spdk_ut_sock_set_priority(struct spdk_sock *_sock, int priority) 255 { 256 return 0; 257 } 258 259 static struct spdk_sock_group_impl * 260 spdk_ut_sock_group_impl_create(void) 261 { 262 struct spdk_ut_sock_group_impl *group_impl; 263 264 group_impl = calloc(1, sizeof(*group_impl)); 265 SPDK_CU_ASSERT_FATAL(group_impl != NULL); 266 267 return &group_impl->base; 268 } 269 270 static int 271 spdk_ut_sock_group_impl_add_sock(struct spdk_sock_group_impl *_group, struct spdk_sock *_sock) 272 { 273 struct spdk_ut_sock_group_impl *group = __ut_group(_group); 274 struct spdk_ut_sock *sock = __ut_sock(_sock); 275 276 group->sock = sock; 277 278 return 0; 279 } 280 281 static int 282 spdk_ut_sock_group_impl_remove_sock(struct spdk_sock_group_impl *_group, struct spdk_sock *_sock) 283 { 284 struct spdk_ut_sock_group_impl *group = __ut_group(_group); 285 struct spdk_ut_sock *sock = __ut_sock(_sock); 286 287 CU_ASSERT(group->sock == sock); 288 group->sock = NULL; 289 290 return 0; 291 } 292 293 static int 294 spdk_ut_sock_group_impl_poll(struct spdk_sock_group_impl *_group, int max_events, 295 struct spdk_sock **socks) 296 { 297 struct spdk_ut_sock_group_impl *group = __ut_group(_group); 298 299 if (group->sock != NULL && group->sock->bytes_avail > 0) { 300 socks[0] = &group->sock->base; 301 return 1; 302 } 303 304 return 0; 305 } 306 307 static int 308 spdk_ut_sock_group_impl_close(struct spdk_sock_group_impl *_group) 309 { 310 struct spdk_ut_sock_group_impl *group = __ut_group(_group); 311 312 CU_ASSERT(group->sock == NULL); 313 314 return 0; 315 } 316 317 static struct spdk_net_impl g_ut_net_impl = { 318 .name = "ut", 319 .getaddr = spdk_ut_sock_getaddr, 320 .connect = spdk_ut_sock_connect, 321 .listen = spdk_ut_sock_listen, 322 .accept = spdk_ut_sock_accept, 323 .close = spdk_ut_sock_close, 324 .recv = spdk_ut_sock_recv, 325 .readv = spdk_ut_sock_readv, 326 .writev = spdk_ut_sock_writev, 327 .set_recvlowat = spdk_ut_sock_set_recvlowat, 328 .set_recvbuf = spdk_ut_sock_set_recvbuf, 329 .set_sendbuf = spdk_ut_sock_set_sendbuf, 330 .set_priority = spdk_ut_sock_set_priority, 331 .is_ipv6 = spdk_ut_sock_is_ipv6, 332 .is_ipv4 = spdk_ut_sock_is_ipv4, 333 .get_placement_id = spdk_ut_sock_get_placement_id, 334 .group_impl_create = spdk_ut_sock_group_impl_create, 335 .group_impl_add_sock = spdk_ut_sock_group_impl_add_sock, 336 .group_impl_remove_sock = spdk_ut_sock_group_impl_remove_sock, 337 .group_impl_poll = spdk_ut_sock_group_impl_poll, 338 .group_impl_close = spdk_ut_sock_group_impl_close, 339 }; 340 341 SPDK_NET_IMPL_REGISTER(ut, &g_ut_net_impl); 342 343 static void 344 _sock(const char *ip, int port) 345 { 346 struct spdk_sock *listen_sock; 347 struct spdk_sock *server_sock; 348 struct spdk_sock *client_sock; 349 char *test_string = "abcdef"; 350 char buffer[64]; 351 ssize_t bytes_read, bytes_written; 352 struct iovec iov; 353 int rc; 354 355 listen_sock = spdk_sock_listen(ip, port); 356 SPDK_CU_ASSERT_FATAL(listen_sock != NULL); 357 358 server_sock = spdk_sock_accept(listen_sock); 359 CU_ASSERT(server_sock == NULL); 360 CU_ASSERT(errno == EAGAIN || errno == EWOULDBLOCK); 361 362 client_sock = spdk_sock_connect(ip, port); 363 SPDK_CU_ASSERT_FATAL(client_sock != NULL); 364 365 /* 366 * Delay a bit here before checking if server socket is 367 * ready. 368 */ 369 usleep(1000); 370 371 server_sock = spdk_sock_accept(listen_sock); 372 SPDK_CU_ASSERT_FATAL(server_sock != NULL); 373 374 /* Test spdk_sock_recv */ 375 iov.iov_base = test_string; 376 iov.iov_len = 7; 377 bytes_written = spdk_sock_writev(client_sock, &iov, 1); 378 CU_ASSERT(bytes_written == 7); 379 380 usleep(1000); 381 382 bytes_read = spdk_sock_recv(server_sock, buffer, 2); 383 CU_ASSERT(bytes_read == 2); 384 385 usleep(1000); 386 387 bytes_read += spdk_sock_recv(server_sock, buffer + 2, 5); 388 CU_ASSERT(bytes_read == 7); 389 390 CU_ASSERT(strncmp(test_string, buffer, 7) == 0); 391 392 /* Test spdk_sock_readv */ 393 iov.iov_base = test_string; 394 iov.iov_len = 7; 395 bytes_written = spdk_sock_writev(client_sock, &iov, 1); 396 CU_ASSERT(bytes_written == 7); 397 398 usleep(1000); 399 400 iov.iov_base = buffer; 401 iov.iov_len = 2; 402 bytes_read = spdk_sock_readv(server_sock, &iov, 1); 403 CU_ASSERT(bytes_read == 2); 404 405 usleep(1000); 406 407 iov.iov_base = buffer + 2; 408 iov.iov_len = 5; 409 bytes_read += spdk_sock_readv(server_sock, &iov, 1); 410 CU_ASSERT(bytes_read == 7); 411 412 usleep(1000); 413 414 CU_ASSERT(strncmp(test_string, buffer, 7) == 0); 415 416 rc = spdk_sock_close(&client_sock); 417 CU_ASSERT(client_sock == NULL); 418 CU_ASSERT(rc == 0); 419 420 rc = spdk_sock_close(&server_sock); 421 CU_ASSERT(server_sock == NULL); 422 CU_ASSERT(rc == 0); 423 424 rc = spdk_sock_close(&listen_sock); 425 CU_ASSERT(listen_sock == NULL); 426 CU_ASSERT(rc == 0); 427 } 428 429 static void 430 posix_sock(void) 431 { 432 _sock("127.0.0.1", UT_PORT); 433 } 434 435 static void 436 ut_sock(void) 437 { 438 _sock(UT_IP, UT_PORT); 439 } 440 441 static void 442 read_data(void *cb_arg, struct spdk_sock_group *group, struct spdk_sock *sock) 443 { 444 struct spdk_sock *server_sock = cb_arg; 445 446 CU_ASSERT(server_sock == sock); 447 448 g_read_data_called = true; 449 g_bytes_read += spdk_sock_recv(server_sock, g_buf + g_bytes_read, sizeof(g_buf) - g_bytes_read); 450 } 451 452 static void 453 _sock_group(const char *ip, int port) 454 { 455 struct spdk_sock_group *group; 456 struct spdk_sock *listen_sock; 457 struct spdk_sock *server_sock; 458 struct spdk_sock *client_sock; 459 char *test_string = "abcdef"; 460 ssize_t bytes_written; 461 struct iovec iov; 462 int rc; 463 464 listen_sock = spdk_sock_listen(ip, port); 465 SPDK_CU_ASSERT_FATAL(listen_sock != NULL); 466 467 server_sock = spdk_sock_accept(listen_sock); 468 CU_ASSERT(server_sock == NULL); 469 CU_ASSERT(errno == EAGAIN || errno == EWOULDBLOCK); 470 471 client_sock = spdk_sock_connect(ip, port); 472 SPDK_CU_ASSERT_FATAL(client_sock != NULL); 473 474 usleep(1000); 475 476 server_sock = spdk_sock_accept(listen_sock); 477 SPDK_CU_ASSERT_FATAL(server_sock != NULL); 478 479 group = spdk_sock_group_create(NULL); 480 SPDK_CU_ASSERT_FATAL(group != NULL); 481 482 /* pass null cb_fn */ 483 rc = spdk_sock_group_add_sock(group, server_sock, NULL, NULL); 484 CU_ASSERT(rc == -1); 485 CU_ASSERT(errno == EINVAL); 486 487 rc = spdk_sock_group_add_sock(group, server_sock, read_data, server_sock); 488 CU_ASSERT(rc == 0); 489 490 /* try adding sock a second time */ 491 rc = spdk_sock_group_add_sock(group, server_sock, read_data, server_sock); 492 CU_ASSERT(rc == -1); 493 CU_ASSERT(errno == EBUSY); 494 495 g_read_data_called = false; 496 g_bytes_read = 0; 497 rc = spdk_sock_group_poll(group); 498 499 CU_ASSERT(rc == 0); 500 CU_ASSERT(g_read_data_called == false); 501 502 iov.iov_base = test_string; 503 iov.iov_len = 7; 504 bytes_written = spdk_sock_writev(client_sock, &iov, 1); 505 CU_ASSERT(bytes_written == 7); 506 507 usleep(1000); 508 509 g_read_data_called = false; 510 g_bytes_read = 0; 511 rc = spdk_sock_group_poll(group); 512 513 CU_ASSERT(rc == 1); 514 CU_ASSERT(g_read_data_called == true); 515 CU_ASSERT(g_bytes_read == 7); 516 517 CU_ASSERT(strncmp(test_string, g_buf, 7) == 0); 518 519 rc = spdk_sock_close(&client_sock); 520 CU_ASSERT(client_sock == NULL); 521 CU_ASSERT(rc == 0); 522 523 /* Try to close sock_group while it still has sockets. */ 524 rc = spdk_sock_group_close(&group); 525 CU_ASSERT(rc == -1); 526 CU_ASSERT(errno == EBUSY); 527 528 /* Try to close sock while it is still part of a sock_group. */ 529 rc = spdk_sock_close(&server_sock); 530 CU_ASSERT(rc == -1); 531 CU_ASSERT(errno == EBUSY); 532 533 rc = spdk_sock_group_remove_sock(group, server_sock); 534 CU_ASSERT(rc == 0); 535 536 rc = spdk_sock_group_close(&group); 537 CU_ASSERT(group == NULL); 538 CU_ASSERT(rc == 0); 539 540 rc = spdk_sock_close(&server_sock); 541 CU_ASSERT(server_sock == NULL); 542 CU_ASSERT(rc == 0); 543 544 rc = spdk_sock_close(&listen_sock); 545 CU_ASSERT(listen_sock == NULL); 546 CU_ASSERT(rc == 0); 547 } 548 549 static void 550 posix_sock_group(void) 551 { 552 _sock_group("127.0.0.1", UT_PORT); 553 } 554 555 static void 556 ut_sock_group(void) 557 { 558 _sock_group(UT_IP, UT_PORT); 559 } 560 561 static void 562 read_data_fairness(void *cb_arg, struct spdk_sock_group *group, struct spdk_sock *sock) 563 { 564 struct spdk_sock *server_sock = cb_arg; 565 ssize_t bytes_read; 566 char buf[1]; 567 568 CU_ASSERT(g_server_sock_read == NULL); 569 CU_ASSERT(server_sock == sock); 570 571 g_server_sock_read = server_sock; 572 bytes_read = spdk_sock_recv(server_sock, buf, 1); 573 CU_ASSERT(bytes_read == 1); 574 } 575 576 static void 577 posix_sock_group_fairness(void) 578 { 579 struct spdk_sock_group *group; 580 struct spdk_sock *listen_sock; 581 struct spdk_sock *server_sock[3]; 582 struct spdk_sock *client_sock[3]; 583 char test_char = 'a'; 584 ssize_t bytes_written; 585 struct iovec iov; 586 int i, rc; 587 588 listen_sock = spdk_sock_listen("127.0.0.1", UT_PORT); 589 SPDK_CU_ASSERT_FATAL(listen_sock != NULL); 590 591 group = spdk_sock_group_create(NULL); 592 SPDK_CU_ASSERT_FATAL(group != NULL); 593 594 for (i = 0; i < 3; i++) { 595 client_sock[i] = spdk_sock_connect("127.0.0.1", UT_PORT); 596 SPDK_CU_ASSERT_FATAL(client_sock[i] != NULL); 597 598 usleep(1000); 599 600 server_sock[i] = spdk_sock_accept(listen_sock); 601 SPDK_CU_ASSERT_FATAL(server_sock[i] != NULL); 602 603 rc = spdk_sock_group_add_sock(group, server_sock[i], 604 read_data_fairness, server_sock[i]); 605 CU_ASSERT(rc == 0); 606 } 607 608 iov.iov_base = &test_char; 609 iov.iov_len = 1; 610 611 for (i = 0; i < 3; i++) { 612 bytes_written = spdk_sock_writev(client_sock[i], &iov, 1); 613 CU_ASSERT(bytes_written == 1); 614 } 615 616 usleep(1000); 617 618 /* 619 * Poll for just one event - this should be server sock 0, since that 620 * is the peer of the first client sock that we wrote to. 621 */ 622 g_server_sock_read = NULL; 623 rc = spdk_sock_group_poll_count(group, 1); 624 CU_ASSERT(rc == 1); 625 CU_ASSERT(g_server_sock_read == server_sock[0]); 626 627 /* 628 * Now write another byte to client sock 0. We want to ensure that 629 * the sock group does not unfairly process the event for this sock 630 * before the socks that were written to earlier. 631 */ 632 bytes_written = spdk_sock_writev(client_sock[0], &iov, 1); 633 CU_ASSERT(bytes_written == 1); 634 635 usleep(1000); 636 637 g_server_sock_read = NULL; 638 rc = spdk_sock_group_poll_count(group, 1); 639 CU_ASSERT(rc == 1); 640 CU_ASSERT(g_server_sock_read == server_sock[1]); 641 642 g_server_sock_read = NULL; 643 rc = spdk_sock_group_poll_count(group, 1); 644 CU_ASSERT(rc == 1); 645 CU_ASSERT(g_server_sock_read == server_sock[2]); 646 647 g_server_sock_read = NULL; 648 rc = spdk_sock_group_poll_count(group, 1); 649 CU_ASSERT(rc == 1); 650 CU_ASSERT(g_server_sock_read == server_sock[0]); 651 652 for (i = 0; i < 3; i++) { 653 rc = spdk_sock_group_remove_sock(group, server_sock[i]); 654 CU_ASSERT(rc == 0); 655 656 rc = spdk_sock_close(&client_sock[i]); 657 CU_ASSERT(client_sock[i] == NULL); 658 CU_ASSERT(rc == 0); 659 660 rc = spdk_sock_close(&server_sock[i]); 661 CU_ASSERT(server_sock[i] == NULL); 662 CU_ASSERT(rc == 0); 663 } 664 665 rc = spdk_sock_group_close(&group); 666 CU_ASSERT(group == NULL); 667 CU_ASSERT(rc == 0); 668 669 rc = spdk_sock_close(&listen_sock); 670 CU_ASSERT(listen_sock == NULL); 671 CU_ASSERT(rc == 0); 672 } 673 674 int 675 main(int argc, char **argv) 676 { 677 CU_pSuite suite = NULL; 678 unsigned int num_failures; 679 680 if (CU_initialize_registry() != CUE_SUCCESS) { 681 return CU_get_error(); 682 } 683 684 suite = CU_add_suite("sock", NULL, NULL); 685 if (suite == NULL) { 686 CU_cleanup_registry(); 687 return CU_get_error(); 688 } 689 690 if ( 691 CU_add_test(suite, "posix_sock", posix_sock) == NULL || 692 CU_add_test(suite, "ut_sock", ut_sock) == NULL || 693 CU_add_test(suite, "posix_sock_group", posix_sock_group) == NULL || 694 CU_add_test(suite, "ut_sock_group", ut_sock_group) == NULL || 695 CU_add_test(suite, "posix_sock_group_fairness", posix_sock_group_fairness) == NULL) { 696 CU_cleanup_registry(); 697 return CU_get_error(); 698 } 699 700 CU_basic_set_mode(CU_BRM_VERBOSE); 701 702 CU_basic_run_tests(); 703 704 num_failures = CU_get_number_of_failures(); 705 CU_cleanup_registry(); 706 707 return num_failures; 708 } 709