| /* |
| * Copyright (C) 2012-2013 ProFUSION embedded systems |
| * |
| * This program is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public |
| * License as published by the Free Software Foundation; either |
| * version 2.1 of the License, or (at your option) any later version. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Lesser General Public License for more details. |
| * |
| * You should have received a copy of the GNU Lesser General Public |
| * License along with this library; if not, see <http://www.gnu.org/licenses/>. |
| */ |
| |
| #pragma once |
| |
| #include <stdbool.h> |
| #include <stdarg.h> |
| #include <stdio.h> |
| |
| #include <shared/macro.h> |
| |
| struct test; |
| typedef int (*testfunc)(const struct test *t); |
| |
| enum test_config { |
| /* |
| * Where's the roots dir for this test. It will LD_PRELOAD path.so in |
| * order to trap calls to functions using paths. |
| */ |
| TC_ROOTFS = 0, |
| |
| /* |
| * What's the desired string to be returned by `uname -r`. It will |
| * trap calls to uname(3P) by LD_PRELOAD'ing uname.so and then filling |
| * in the information in u.release. |
| */ |
| TC_UNAME_R, |
| |
| /* |
| * Fake calls to init_module(2), returning return-code and setting |
| * errno to err-code. Set this variable with the following format: |
| * |
| * modname:return-code:err-code |
| * |
| * When this variable is used, all calls to init_module() are trapped |
| * and by default the return code is 0. In other words, they fake |
| * "success" for all modules, except the ones in the list above, for |
| * which the return codes are used. |
| */ |
| TC_INIT_MODULE_RETCODES, |
| |
| /* |
| * Fake calls to delete_module(2), returning return-code and setting |
| * errno to err-code. Set this variable with the following format: |
| * |
| * modname:return-code:err-code |
| * |
| * When this variable is used, all calls to init_module() are trapped |
| * and by default the return code is 0. In other words, they fake |
| * "success" for all modules, except the ones in the list above, for |
| * which the return codes are used. |
| */ |
| TC_DELETE_MODULE_RETCODES, |
| |
| _TC_LAST, |
| }; |
| |
| #define S_TC_ROOTFS "TESTSUITE_ROOTFS" |
| #define S_TC_UNAME_R "TESTSUITE_UNAME_R" |
| #define S_TC_INIT_MODULE_RETCODES "TESTSUITE_INIT_MODULE_RETCODES" |
| #define S_TC_DELETE_MODULE_RETCODES "TESTSUITE_DELETE_MODULE_RETCODES" |
| |
| struct keyval { |
| const char *key; |
| const char *val; |
| }; |
| |
| struct test { |
| const char *name; |
| const char *description; |
| struct { |
| /* File with correct stdout */ |
| const char *out; |
| /* File with correct stderr */ |
| const char *err; |
| |
| /* |
| * whether to treat the correct files as regex to the real |
| * output |
| */ |
| bool regex; |
| |
| /* |
| * Vector with pair of files |
| * key = correct file |
| * val = file to check |
| */ |
| const struct keyval *files; |
| } output; |
| /* comma-separated list of loaded modules at the end of the test */ |
| const char *modules_loaded; |
| testfunc func; |
| const char *config[_TC_LAST]; |
| const char *path; |
| const struct keyval *env_vars; |
| bool need_spawn; |
| bool expected_fail; |
| /* allow to skip tests that don't meet compile-time dependencies */ |
| bool skip; |
| bool print_outputs; |
| } __attribute__((aligned(8))); |
| |
| |
| int test_init(const struct test *start, const struct test *stop, |
| int argc, char *const argv[]); |
| const struct test *test_find(const struct test *start, const struct test *stop, |
| const char *name); |
| int test_spawn_prog(const char *prog, const char *const args[]); |
| int test_run(const struct test *t); |
| |
| #define TS_EXPORT __attribute__ ((visibility("default"))) |
| |
| #define _LOG(prefix, fmt, ...) printf("TESTSUITE: " prefix fmt, ## __VA_ARGS__) |
| #define LOG(fmt, ...) _LOG("", fmt, ## __VA_ARGS__) |
| #define WARN(fmt, ...) _LOG("WARN: ", fmt, ## __VA_ARGS__) |
| #define ERR(fmt, ...) _LOG("ERR: ", fmt, ## __VA_ARGS__) |
| |
| #define assert_return(expr, r) \ |
| do { \ |
| if ((!(expr))) { \ |
| ERR("Failed assertion: " #expr " %s:%d %s\n", \ |
| __FILE__, __LINE__, __PRETTY_FUNCTION__); \ |
| return (r); \ |
| } \ |
| } while (false) |
| |
| |
| /* Test definitions */ |
| #define DEFINE_TEST_WITH_FUNC(_name, _func, ...) \ |
| static const struct test UNIQ(s##_name) \ |
| __attribute__((used, section("kmod_tests"), aligned(8))) = { \ |
| .name = #_name, \ |
| .func = _func, \ |
| ## __VA_ARGS__ \ |
| }; |
| |
| #define DEFINE_TEST(_name, ...) DEFINE_TEST_WITH_FUNC(_name, _name, __VA_ARGS__) |
| |
| #define TESTSUITE_MAIN() \ |
| extern struct test __start_kmod_tests[] __attribute__((weak, visibility("hidden"))); \ |
| extern struct test __stop_kmod_tests[] __attribute__((weak, visibility("hidden"))); \ |
| int main(int argc, char *argv[]) \ |
| { \ |
| const struct test *t; \ |
| int arg; \ |
| \ |
| arg = test_init(__start_kmod_tests, __stop_kmod_tests, argc, argv); \ |
| if (arg == 0) \ |
| return 0; \ |
| if (arg < 0) \ |
| return EXIT_FAILURE; \ |
| \ |
| if (arg < argc) { \ |
| t = test_find(__start_kmod_tests, __stop_kmod_tests, argv[arg]); \ |
| if (t == NULL) { \ |
| fprintf(stderr, "could not find test %s\n", argv[arg]); \ |
| exit(EXIT_FAILURE); \ |
| } \ |
| \ |
| return test_run(t); \ |
| } \ |
| \ |
| for (t = __start_kmod_tests; t < __stop_kmod_tests; t++) { \ |
| if (test_run(t) != 0) \ |
| exit(EXIT_FAILURE); \ |
| } \ |
| \ |
| exit(EXIT_SUCCESS); \ |
| } \ |
| |
| #ifdef noreturn |
| # define __noreturn noreturn |
| #elif __STDC_VERSION__ >= 201112L |
| # define __noreturn _Noreturn |
| #else |
| # define __noreturn __attribute__((noreturn)) |
| #endif |