Annotation of embedaddon/strongswan/src/libstrongswan/tests/test_runner.c, revision 1.1
1.1 ! misho 1: /*
! 2: * Copyright (C) 2013 Tobias Brunner
! 3: * HSR Hochschule fuer Technik Rapperswil
! 4: * Copyright (C) 2013 Martin Willi
! 5: * Copyright (C) 2013 revosec AG
! 6: *
! 7: * This program is free software; you can redistribute it and/or modify it
! 8: * under the terms of the GNU General Public License as published by the
! 9: * Free Software Foundation; either version 2 of the License, or (at your
! 10: * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
! 11: *
! 12: * This program is distributed in the hope that it will be useful, but
! 13: * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
! 14: * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
! 15: * for more details.
! 16: */
! 17:
! 18: #include "test_runner.h"
! 19:
! 20: #include <library.h>
! 21: #include <threading/thread.h>
! 22: #include <plugins/plugin_feature.h>
! 23: #include <collections/array.h>
! 24: #include <utils/test.h>
! 25:
! 26: #include <stdlib.h>
! 27: #include <dirent.h>
! 28: #include <unistd.h>
! 29: #include <limits.h>
! 30: #include <stdlib.h>
! 31:
! 32: /**
! 33: * Get a tty color escape character for stderr
! 34: */
! 35: #define TTY(color) tty_escape_get(2, TTY_FG_##color)
! 36:
! 37: /**
! 38: * A global symbol indicating libtest linkage
! 39: */
! 40: #ifdef WIN32
! 41: __declspec(dllexport)
! 42: #endif
! 43: bool test_runner_available = TRUE;
! 44:
! 45: /**
! 46: * Destroy a single test suite and associated data
! 47: */
! 48: static void destroy_suite(test_suite_t *suite)
! 49: {
! 50: test_case_t *tcase;
! 51:
! 52: while (array_remove(suite->tcases, 0, &tcase))
! 53: {
! 54: array_destroy(tcase->functions);
! 55: array_destroy(tcase->fixtures);
! 56: }
! 57: free(suite);
! 58: }
! 59:
! 60: /**
! 61: * Filter loaded test suites, either remove suites listed (exclude=TRUE), or all
! 62: * that are not listed (exclude=FALSE).
! 63: */
! 64: static void apply_filter(array_t *loaded, char *filter, bool exclude)
! 65: {
! 66: enumerator_t *enumerator, *names;
! 67: hashtable_t *listed;
! 68: test_suite_t *suite;
! 69: char *name;
! 70:
! 71: listed = hashtable_create(hashtable_hash_str, hashtable_equals_str, 8);
! 72: names = enumerator_create_token(filter, ",", " ");
! 73: while (names->enumerate(names, &name))
! 74: {
! 75: listed->put(listed, name, name);
! 76: }
! 77: enumerator = array_create_enumerator(loaded);
! 78: while (enumerator->enumerate(enumerator, &suite))
! 79: {
! 80: if ((exclude && listed->get(listed, suite->name)) ||
! 81: (!exclude && !listed->get(listed, suite->name)))
! 82: {
! 83: array_remove_at(loaded, enumerator);
! 84: destroy_suite(suite);
! 85: }
! 86: }
! 87: enumerator->destroy(enumerator);
! 88: listed->destroy(listed);
! 89: names->destroy(names);
! 90: }
! 91:
! 92: /**
! 93: * Check if the given string is contained in the filter string.
! 94: */
! 95: static bool is_in_filter(const char *find, char *filter)
! 96: {
! 97: enumerator_t *names;
! 98: bool found = FALSE;
! 99: char *name;
! 100:
! 101: names = enumerator_create_token(filter, ",", " ");
! 102: while (names->enumerate(names, &name))
! 103: {
! 104: if (streq(name, find))
! 105: {
! 106: found = TRUE;
! 107: break;
! 108: }
! 109: }
! 110: names->destroy(names);
! 111: return found;
! 112: }
! 113:
! 114: /**
! 115: * Removes and destroys test suites that are not selected or
! 116: * explicitly excluded.
! 117: */
! 118: static void filter_suites(array_t *loaded)
! 119: {
! 120: char *filter;
! 121:
! 122: filter = getenv("TESTS_SUITES");
! 123: if (filter)
! 124: {
! 125: apply_filter(loaded, filter, FALSE);
! 126: }
! 127: filter = getenv("TESTS_SUITES_EXCLUDE");
! 128: if (filter)
! 129: {
! 130: apply_filter(loaded, filter, TRUE);
! 131: }
! 132: }
! 133:
! 134: /**
! 135: * Load all available test suites, or optionally only selected ones.
! 136: */
! 137: static array_t *load_suites(test_configuration_t configs[],
! 138: test_runner_init_t init, char *cfg)
! 139: {
! 140: array_t *suites;
! 141: bool old = FALSE;
! 142: int i;
! 143:
! 144: library_init(cfg, "test-runner");
! 145:
! 146: test_setup_handler();
! 147:
! 148: if (init && !init(TRUE))
! 149: {
! 150: library_deinit();
! 151: return NULL;
! 152: }
! 153: lib->plugins->status(lib->plugins, LEVEL_CTRL);
! 154:
! 155: if (lib->leak_detective)
! 156: {
! 157: old = lib->leak_detective->set_state(lib->leak_detective, FALSE);
! 158: }
! 159:
! 160: suites = array_create(0, 0);
! 161:
! 162: for (i = 0; configs[i].suite; i++)
! 163: {
! 164: if (configs[i].feature.type == 0 ||
! 165: lib->plugins->has_feature(lib->plugins, configs[i].feature))
! 166: {
! 167: array_insert(suites, -1, configs[i].suite());
! 168: }
! 169: }
! 170: filter_suites(suites);
! 171:
! 172: if (lib->leak_detective)
! 173: {
! 174: lib->leak_detective->set_state(lib->leak_detective, old);
! 175: }
! 176:
! 177: if (init)
! 178: {
! 179: init(FALSE);
! 180: }
! 181: library_deinit();
! 182:
! 183: return suites;
! 184: }
! 185:
! 186: /**
! 187: * Unload and destroy test suites and associated data
! 188: */
! 189: static void unload_suites(array_t *suites)
! 190: {
! 191: test_suite_t *suite;
! 192:
! 193: while (array_remove(suites, 0, &suite))
! 194: {
! 195: destroy_suite(suite);
! 196: }
! 197: array_destroy(suites);
! 198: }
! 199:
! 200: /**
! 201: * Run a single test function, return FALSE on failure
! 202: */
! 203: static bool run_test(test_function_t *tfun, int i)
! 204: {
! 205: if (test_restore_point())
! 206: {
! 207: tfun->cb(i);
! 208: return TRUE;
! 209: }
! 210: thread_cleanup_popall();
! 211: return FALSE;
! 212: }
! 213:
! 214: /**
! 215: * Invoke fixture setup/teardown
! 216: */
! 217: static bool call_fixture(test_case_t *tcase, bool up)
! 218: {
! 219: enumerator_t *enumerator;
! 220: test_fixture_t *fixture;
! 221: bool failure = FALSE;
! 222:
! 223: enumerator = array_create_enumerator(tcase->fixtures);
! 224: while (enumerator->enumerate(enumerator, &fixture))
! 225: {
! 226: if (test_restore_point())
! 227: {
! 228: if (up)
! 229: {
! 230: if (fixture->setup)
! 231: {
! 232: fixture->setup();
! 233: }
! 234: }
! 235: else
! 236: {
! 237: if (fixture->teardown)
! 238: {
! 239: fixture->teardown();
! 240: }
! 241: }
! 242: }
! 243: else
! 244: {
! 245: thread_cleanup_popall();
! 246: failure = TRUE;
! 247: break;
! 248: }
! 249: }
! 250: enumerator->destroy(enumerator);
! 251:
! 252: return !failure;
! 253: }
! 254:
! 255: /**
! 256: * Test initialization, initializes libstrongswan for the next run
! 257: */
! 258: static bool pre_test(test_runner_init_t init, char *cfg)
! 259: {
! 260: library_init(cfg, "test-runner");
! 261:
! 262: /* use non-blocking RNG to generate keys fast */
! 263: lib->settings->set_default_str(lib->settings,
! 264: "libstrongswan.plugins.random.random",
! 265: lib->settings->get_str(lib->settings,
! 266: "libstrongswan.plugins.random.urandom", "/dev/urandom"));
! 267: /* same for the gcrypt plugin */
! 268: lib->settings->set_default_str(lib->settings,
! 269: "libstrongswan.plugins.gcrypt.quick_random", "yes");
! 270:
! 271: if (lib->leak_detective)
! 272: {
! 273: /* disable leak reports during testing */
! 274: lib->leak_detective->set_report_cb(lib->leak_detective,
! 275: NULL, NULL, NULL);
! 276: }
! 277: if (init && !init(TRUE))
! 278: {
! 279: library_deinit();
! 280: return FALSE;
! 281: }
! 282: return TRUE;
! 283: }
! 284:
! 285: /**
! 286: * Failure description
! 287: */
! 288: typedef struct {
! 289: char *name;
! 290: char msg[4096 - sizeof(char*) - 2 * sizeof(int)];
! 291: const char *file;
! 292: int line;
! 293: int i;
! 294: backtrace_t *bt;
! 295: } failure_t;
! 296:
! 297: /**
! 298: * Data passed to leak report callbacks
! 299: */
! 300: typedef struct {
! 301: array_t *failures;
! 302: char *name;
! 303: int i;
! 304: int leaks;
! 305: } report_data_t;
! 306:
! 307: /**
! 308: * Leak report callback, build failures from leaks
! 309: */
! 310: static void report_leaks(report_data_t *data, int count, size_t bytes,
! 311: backtrace_t *bt, bool detailed)
! 312: {
! 313: failure_t failure = {
! 314: .name = data->name,
! 315: .i = data->i,
! 316: .bt = bt->clone(bt),
! 317: };
! 318:
! 319: snprintf(failure.msg, sizeof(failure.msg),
! 320: "Leak detected: %d allocations using %zu bytes", count, bytes);
! 321:
! 322: array_insert(data->failures, -1, &failure);
! 323: }
! 324:
! 325: /**
! 326: * Leak summary callback, check if any leaks found
! 327: */
! 328: static void sum_leaks(report_data_t *data, int count, size_t bytes,
! 329: int whitelisted)
! 330: {
! 331: data->leaks = count;
! 332: }
! 333:
! 334: /**
! 335: * Do library cleanup and optionally check for memory leaks
! 336: */
! 337: static bool post_test(test_runner_init_t init, bool check_leaks,
! 338: array_t *failures, char *name, int i, int *leaks)
! 339: {
! 340: report_data_t data = {
! 341: .failures = failures,
! 342: .name = name,
! 343: .i = i,
! 344: };
! 345:
! 346: if (init)
! 347: {
! 348: if (test_restore_point())
! 349: {
! 350: init(FALSE);
! 351: }
! 352: else
! 353: {
! 354: thread_cleanup_popall();
! 355: library_deinit();
! 356: return FALSE;
! 357: }
! 358: }
! 359: if (check_leaks && lib->leak_detective)
! 360: {
! 361: lib->leak_detective->set_report_cb(lib->leak_detective,
! 362: (leak_detective_report_cb_t)report_leaks,
! 363: (leak_detective_summary_cb_t)sum_leaks, &data);
! 364: }
! 365: library_deinit();
! 366:
! 367: *leaks = data.leaks;
! 368: return TRUE;
! 369: }
! 370:
! 371: /**
! 372: * Collect failure information, add failure_t to array
! 373: */
! 374: static void collect_failure_info(array_t *failures, char *name, int i)
! 375: {
! 376: failure_t failure = {
! 377: .name = name,
! 378: .i = i,
! 379: .bt = test_failure_backtrace(),
! 380: };
! 381:
! 382: failure.line = test_failure_get(failure.msg, sizeof(failure.msg),
! 383: &failure.file);
! 384:
! 385: array_insert(failures, -1, &failure);
! 386: }
! 387:
! 388: /**
! 389: * Collect warning information, add failure_t to array
! 390: */
! 391: static bool collect_warning_info(array_t *warnings, char *name, int i)
! 392: {
! 393: failure_t warning = {
! 394: .name = name,
! 395: .i = i,
! 396: };
! 397:
! 398: warning.line = test_warning_get(warning.msg, sizeof(warning.msg),
! 399: &warning.file);
! 400: if (warning.line)
! 401: {
! 402: array_insert(warnings, -1, &warning);
! 403: }
! 404: return warning.line;
! 405: }
! 406:
! 407: /**
! 408: * Print array of collected failure_t to stderr
! 409: */
! 410: static void print_failures(array_t *failures, bool warnings)
! 411: {
! 412: failure_t failure;
! 413:
! 414: threads_init();
! 415: backtrace_init();
! 416:
! 417: while (array_remove(failures, 0, &failure))
! 418: {
! 419: if (warnings)
! 420: {
! 421: fprintf(stderr, " %sWarning in '%s': %s (",
! 422: TTY(YELLOW), failure.name, failure.msg);
! 423: }
! 424: else
! 425: {
! 426: fprintf(stderr, " %sFailure in '%s': %s (",
! 427: TTY(RED), failure.name, failure.msg);
! 428: }
! 429: if (failure.line)
! 430: {
! 431: fprintf(stderr, "%s:%d, ", failure.file, failure.line);
! 432: }
! 433: fprintf(stderr, "i = %d)%s\n", failure.i, TTY(DEF));
! 434: if (failure.bt)
! 435: {
! 436: failure.bt->log(failure.bt, stderr, TRUE);
! 437: failure.bt->destroy(failure.bt);
! 438: }
! 439: }
! 440:
! 441: backtrace_deinit();
! 442: threads_deinit();
! 443: }
! 444:
! 445: /**
! 446: * Run a single test case with fixtures
! 447: */
! 448: static bool run_case(test_case_t *tcase, test_runner_init_t init, char *cfg)
! 449: {
! 450: enumerator_t *enumerator;
! 451: test_function_t *tfun;
! 452: int passed = 0;
! 453: array_t *failures, *warnings;
! 454:
! 455: failures = array_create(sizeof(failure_t), 0);
! 456: warnings = array_create(sizeof(failure_t), 0);
! 457:
! 458: fprintf(stderr, " Running case '%s': ", tcase->name);
! 459: fflush(stderr);
! 460:
! 461: enumerator = array_create_enumerator(tcase->functions);
! 462: while (enumerator->enumerate(enumerator, &tfun))
! 463: {
! 464: int i, rounds = 0;
! 465:
! 466: for (i = tfun->start; i < tfun->end; i++)
! 467: {
! 468: if (pre_test(init, cfg))
! 469: {
! 470: bool ok = FALSE;
! 471: int leaks = 0;
! 472:
! 473: test_setup_timeout(tcase->timeout);
! 474:
! 475: if (call_fixture(tcase, TRUE))
! 476: {
! 477: if (run_test(tfun, i))
! 478: {
! 479: if (call_fixture(tcase, FALSE))
! 480: {
! 481: ok = TRUE;
! 482: }
! 483: }
! 484: else
! 485: {
! 486: call_fixture(tcase, FALSE);
! 487: }
! 488: }
! 489: if (!post_test(init, ok, failures, tfun->name, i, &leaks))
! 490: {
! 491: ok = FALSE;
! 492: }
! 493:
! 494: test_setup_timeout(0);
! 495:
! 496: if (ok)
! 497: {
! 498: if (!leaks)
! 499: {
! 500: rounds++;
! 501: if (!collect_warning_info(warnings, tfun->name, i))
! 502: {
! 503: fprintf(stderr, "%s+%s", TTY(GREEN), TTY(DEF));
! 504: }
! 505: else
! 506: {
! 507: fprintf(stderr, "%s~%s", TTY(YELLOW), TTY(DEF));
! 508: }
! 509: }
! 510: }
! 511: else
! 512: {
! 513: collect_failure_info(failures, tfun->name, i);
! 514: }
! 515: if (!ok || leaks)
! 516: {
! 517: fprintf(stderr, "%s-%s", TTY(RED), TTY(DEF));
! 518: }
! 519: }
! 520: else
! 521: {
! 522: fprintf(stderr, "!");
! 523: }
! 524: }
! 525: fflush(stderr);
! 526: if (rounds == tfun->end - tfun->start)
! 527: {
! 528: passed++;
! 529: }
! 530: }
! 531: enumerator->destroy(enumerator);
! 532:
! 533: fprintf(stderr, "\n");
! 534:
! 535: print_failures(warnings, TRUE);
! 536: print_failures(failures, FALSE);
! 537: array_destroy(failures);
! 538: array_destroy(warnings);
! 539:
! 540: return passed == array_count(tcase->functions);
! 541: }
! 542:
! 543: /**
! 544: * Run a single test suite
! 545: */
! 546: static bool run_suite(test_suite_t *suite, test_runner_init_t init, char *cfg)
! 547: {
! 548: enumerator_t *enumerator;
! 549: test_case_t *tcase;
! 550: int passed = 0;
! 551:
! 552: fprintf(stderr, " Running suite '%s':\n", suite->name);
! 553:
! 554: enumerator = array_create_enumerator(suite->tcases);
! 555: while (enumerator->enumerate(enumerator, &tcase))
! 556: {
! 557: if (run_case(tcase, init, cfg))
! 558: {
! 559: passed++;
! 560: }
! 561: }
! 562: enumerator->destroy(enumerator);
! 563:
! 564: if (passed == array_count(suite->tcases))
! 565: {
! 566: fprintf(stderr, " %sPassed all %u '%s' test cases%s\n",
! 567: TTY(GREEN), array_count(suite->tcases), suite->name, TTY(DEF));
! 568: return TRUE;
! 569: }
! 570: fprintf(stderr, " %sPassed %u/%u '%s' test cases%s\n",
! 571: TTY(RED), passed, array_count(suite->tcases), suite->name, TTY(DEF));
! 572: return FALSE;
! 573: }
! 574:
! 575: /**
! 576: * See header.
! 577: */
! 578: int test_runner_run(const char *name, test_configuration_t configs[],
! 579: test_runner_init_t init)
! 580: {
! 581: array_t *suites;
! 582: test_suite_t *suite;
! 583: enumerator_t *enumerator;
! 584: int passed = 0, result;
! 585: level_t level = LEVEL_SILENT;
! 586: char *cfg, *runners, *verbosity;
! 587:
! 588: /* redirect all output to stderr (to redirect make's stdout to /dev/null) */
! 589: dup2(2, 1);
! 590:
! 591: runners = getenv("TESTS_RUNNERS");
! 592: if (runners && !is_in_filter(name, runners))
! 593: {
! 594: return EXIT_SUCCESS;
! 595: }
! 596:
! 597: cfg = getenv("TESTS_STRONGSWAN_CONF");
! 598:
! 599: suites = load_suites(configs, init, cfg);
! 600: if (!suites)
! 601: {
! 602: return EXIT_FAILURE;
! 603: }
! 604:
! 605: verbosity = getenv("TESTS_VERBOSITY");
! 606: if (verbosity)
! 607: {
! 608: level = atoi(verbosity);
! 609: }
! 610: dbg_default_set_level(level);
! 611:
! 612: fprintf(stderr, "Running %u '%s' test suites:\n", array_count(suites), name);
! 613:
! 614: enumerator = array_create_enumerator(suites);
! 615: while (enumerator->enumerate(enumerator, &suite))
! 616: {
! 617: if (run_suite(suite, init, cfg))
! 618: {
! 619: passed++;
! 620: }
! 621: }
! 622: enumerator->destroy(enumerator);
! 623:
! 624: if (passed == array_count(suites))
! 625: {
! 626: fprintf(stderr, "%sPassed all %u '%s' suites%s\n",
! 627: TTY(GREEN), array_count(suites), name, TTY(DEF));
! 628: result = EXIT_SUCCESS;
! 629: }
! 630: else
! 631: {
! 632: fprintf(stderr, "%sPassed %u of %u '%s' suites%s\n",
! 633: TTY(RED), passed, array_count(suites), name, TTY(DEF));
! 634: result = EXIT_FAILURE;
! 635: }
! 636:
! 637: unload_suites(suites);
! 638:
! 639: return result;
! 640: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>