Annotation of embedaddon/strongswan/src/libstrongswan/tests/test_runner.c, revision 1.1.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>