diff options
Diffstat (limited to 'test/common_plat/common/odp_cunit_common.c')
-rw-r--r-- | test/common_plat/common/odp_cunit_common.c | 373 |
1 files changed, 373 insertions, 0 deletions
diff --git a/test/common_plat/common/odp_cunit_common.c b/test/common_plat/common/odp_cunit_common.c new file mode 100644 index 000000000..2337c92b0 --- /dev/null +++ b/test/common_plat/common/odp_cunit_common.c @@ -0,0 +1,373 @@ +/* Copyright (c) 2014, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <string.h> +#include <odp_api.h> +#include <odp_cunit_common.h> +#include <odp/helper/linux.h> +/* Globals */ +static odph_odpthread_t thread_tbl[MAX_WORKERS]; +static odp_instance_t instance; + +/* + * global init/term functions which may be registered + * defaults to functions performing odp init/term. + */ +static int tests_global_init(odp_instance_t *inst); +static int tests_global_term(odp_instance_t inst); +static struct { + int (*global_init_ptr)(odp_instance_t *inst); + int (*global_term_ptr)(odp_instance_t inst); +} global_init_term = {tests_global_init, tests_global_term}; + +static odp_suiteinfo_t *global_testsuites; + +/** create test thread */ +int odp_cunit_thread_create(int func_ptr(void *), pthrd_arg *arg) +{ + odp_cpumask_t cpumask; + odph_odpthread_params_t thr_params; + + memset(&thr_params, 0, sizeof(thr_params)); + thr_params.start = func_ptr; + thr_params.arg = arg; + thr_params.thr_type = ODP_THREAD_WORKER; + thr_params.instance = instance; + + /* Create and init additional threads */ + odp_cpumask_default_worker(&cpumask, arg->numthrds); + + return odph_odpthreads_create(thread_tbl, &cpumask, &thr_params); +} + +/** exit from test thread */ +int odp_cunit_thread_exit(pthrd_arg *arg) +{ + /* Wait for other threads to exit */ + if (odph_odpthreads_join(thread_tbl) != arg->numthrds) { + fprintf(stderr, + "error: odph_odpthreads_join() failed.\n"); + return -1; + } + + return 0; +} + +static int tests_global_init(odp_instance_t *inst) +{ + if (0 != odp_init_global(inst, NULL, NULL)) { + fprintf(stderr, "error: odp_init_global() failed.\n"); + return -1; + } + if (0 != odp_init_local(*inst, ODP_THREAD_CONTROL)) { + fprintf(stderr, "error: odp_init_local() failed.\n"); + return -1; + } + + return 0; +} + +static int tests_global_term(odp_instance_t inst) +{ + if (0 != odp_term_local()) { + fprintf(stderr, "error: odp_term_local() failed.\n"); + return -1; + } + + if (0 != odp_term_global(inst)) { + fprintf(stderr, "error: odp_term_global() failed.\n"); + return -1; + } + + return 0; +} + +/* + * register tests_global_init and tests_global_term functions. + * If some of these functions are not registered, the defaults functions + * (tests_global_init() and tests_global_term()) defined above are used. + * One should use these register functions when defining these hooks. + * Note that passing NULL as function pointer is valid and will simply + * prevent the default (odp init/term) to be done. + */ +void odp_cunit_register_global_init(int (*func_init_ptr)(odp_instance_t *inst)) +{ + global_init_term.global_init_ptr = func_init_ptr; +} + +void odp_cunit_register_global_term(int (*func_term_ptr)(odp_instance_t inst)) +{ + global_init_term.global_term_ptr = func_term_ptr; +} + +static odp_suiteinfo_t *cunit_get_suite_info(const char *suite_name) +{ + odp_suiteinfo_t *sinfo; + + for (sinfo = global_testsuites; sinfo->pName; sinfo++) + if (strcmp(sinfo->pName, suite_name) == 0) + return sinfo; + + return NULL; +} + +static odp_testinfo_t *cunit_get_test_info(odp_suiteinfo_t *sinfo, + const char *test_name) +{ + odp_testinfo_t *tinfo; + + for (tinfo = sinfo->pTests; tinfo->pName; tinfo++) + if (strcmp(tinfo->pName, test_name) == 0) + return tinfo; + + return NULL; +} + +/* A wrapper for the suite's init function. This is done to allow for a + * potential runtime check to determine whether each test in the suite + * is active (enabled by using ODP_TEST_INFO_CONDITIONAL()). If present, + * the conditional check is run after the suite's init function. + */ +static int _cunit_suite_init(void) +{ + int ret = 0; + CU_pSuite cur_suite = CU_get_current_suite(); + odp_suiteinfo_t *sinfo; + odp_testinfo_t *tinfo; + + /* find the suite currently being run */ + cur_suite = CU_get_current_suite(); + if (!cur_suite) + return -1; + + sinfo = cunit_get_suite_info(cur_suite->pName); + if (!sinfo) + return -1; + + /* execute its init function */ + if (sinfo->pInitFunc) { + ret = sinfo->pInitFunc(); + if (ret) + return ret; + } + + /* run any configured conditional checks and mark inactive tests */ + for (tinfo = sinfo->pTests; tinfo->pName; tinfo++) { + CU_pTest ptest; + CU_ErrorCode err; + + if (!tinfo->check_active || tinfo->check_active()) + continue; + + /* test is inactive, mark it as such */ + ptest = CU_get_test_by_name(tinfo->pName, cur_suite); + if (ptest) + err = CU_set_test_active(ptest, CU_FALSE); + else + err = CUE_NOTEST; + + if (err != CUE_SUCCESS) { + fprintf(stderr, "%s: failed to set test %s inactive\n", + __func__, tinfo->pName); + return -1; + } + } + + return ret; +} + +/* + * Register suites and tests with CUnit. + * + * Similar to CU_register_suites() but using locally defined wrapper + * types. + */ +static int cunit_register_suites(odp_suiteinfo_t testsuites[]) +{ + odp_suiteinfo_t *sinfo; + odp_testinfo_t *tinfo; + CU_pSuite suite; + CU_pTest test; + + for (sinfo = testsuites; sinfo->pName; sinfo++) { + suite = CU_add_suite(sinfo->pName, + _cunit_suite_init, sinfo->pCleanupFunc); + if (!suite) + return CU_get_error(); + + for (tinfo = sinfo->pTests; tinfo->pName; tinfo++) { + test = CU_add_test(suite, tinfo->pName, + tinfo->pTestFunc); + if (!test) + return CU_get_error(); + } + } + + return 0; +} + +static int cunit_update_test(CU_pSuite suite, + odp_suiteinfo_t *sinfo, + odp_testinfo_t *updated_tinfo) +{ + CU_pTest test = NULL; + CU_ErrorCode err; + odp_testinfo_t *tinfo; + const char *test_name = updated_tinfo->pName; + + tinfo = cunit_get_test_info(sinfo, test_name); + if (tinfo) + test = CU_get_test(suite, test_name); + + if (!tinfo || !test) { + fprintf(stderr, "%s: unable to find existing test named %s\n", + __func__, test_name); + return -1; + } + + err = CU_set_test_func(test, updated_tinfo->pTestFunc); + if (err != CUE_SUCCESS) { + fprintf(stderr, "%s: failed to update test func for %s\n", + __func__, test_name); + return -1; + } + + tinfo->check_active = updated_tinfo->check_active; + + return 0; +} + +static int cunit_update_suite(odp_suiteinfo_t *updated_sinfo) +{ + CU_pSuite suite = NULL; + CU_ErrorCode err; + odp_suiteinfo_t *sinfo; + odp_testinfo_t *tinfo; + + /* find previously registered suite with matching name */ + sinfo = cunit_get_suite_info(updated_sinfo->pName); + + if (sinfo) { + /* lookup the associated CUnit suite */ + suite = CU_get_suite_by_name(updated_sinfo->pName, + CU_get_registry()); + } + + if (!sinfo || !suite) { + fprintf(stderr, "%s: unable to find existing suite named %s\n", + __func__, updated_sinfo->pName); + return -1; + } + + sinfo->pInitFunc = updated_sinfo->pInitFunc; + sinfo->pCleanupFunc = updated_sinfo->pCleanupFunc; + + err = CU_set_suite_cleanupfunc(suite, updated_sinfo->pCleanupFunc); + if (err != CUE_SUCCESS) { + fprintf(stderr, "%s: failed to update cleanup func for %s\n", + __func__, updated_sinfo->pName); + return -1; + } + + for (tinfo = updated_sinfo->pTests; tinfo->pName; tinfo++) { + int ret; + + ret = cunit_update_test(suite, sinfo, tinfo); + if (ret != 0) + return ret; + } + + return 0; +} + +/* + * Run tests previously registered via odp_cunit_register() + */ +int odp_cunit_run(void) +{ + int ret; + + printf("\tODP API version: %s\n", odp_version_api_str()); + printf("\tODP implementation name: %s\n", odp_version_impl_name()); + printf("\tODP implementation version: %s\n", odp_version_impl_str()); + + CU_basic_set_mode(CU_BRM_VERBOSE); + CU_basic_run_tests(); + + ret = CU_get_number_of_failure_records(); + + CU_cleanup_registry(); + + /* call test executable terminason hook, if any */ + if (global_init_term.global_term_ptr && + ((*global_init_term.global_term_ptr)(instance) != 0)) + return -1; + + return (ret) ? -1 : 0; +} + +/* + * Update suites/tests previously registered via odp_cunit_register(). + * + * Note that this is intended for modifying the properties of already + * registered suites/tests. New suites/tests can only be registered via + * odp_cunit_register(). + */ +int odp_cunit_update(odp_suiteinfo_t testsuites[]) +{ + int ret = 0; + odp_suiteinfo_t *sinfo; + + for (sinfo = testsuites; sinfo->pName && ret == 0; sinfo++) + ret = cunit_update_suite(sinfo); + + return ret; +} + +/* + * Register test suites to be run via odp_cunit_run() + */ +int odp_cunit_register(odp_suiteinfo_t testsuites[]) +{ + /* call test executable init hook, if any */ + if (global_init_term.global_init_ptr) { + if ((*global_init_term.global_init_ptr)(&instance) == 0) { + /* After ODP initialization, set main thread's + * CPU affinity to the 1st available control CPU core + */ + int cpu = 0; + odp_cpumask_t cpuset; + + odp_cpumask_zero(&cpuset); + if (odp_cpumask_default_control(&cpuset, 1) == 1) { + cpu = odp_cpumask_first(&cpuset); + odph_odpthread_setaffinity(cpu); + } + } else { + /* ODP initialization failed */ + return -1; + } + } + + CU_set_error_action(CUEA_ABORT); + + CU_initialize_registry(); + global_testsuites = testsuites; + cunit_register_suites(testsuites); + CU_set_fail_on_inactive(CU_FALSE); + + return 0; +} + +/* + * Parse command line options to extract options affectiong cunit_common. + * (hence also helpers options as cunit_common uses the helpers) + * Options private to the test calling cunit_common are not parsed here. + */ +int odp_cunit_parse_options(int argc, char *argv[]) +{ + return odph_parse_options(argc, argv, NULL, NULL); +} |